diff --git a/engine/client/in_joy.c b/engine/client/in_joy.c index 08f9ec08..daabc588 100644 --- a/engine/client/in_joy.c +++ b/engine/client/in_joy.c @@ -43,7 +43,6 @@ static struct joy_axis_s short val; short prevval; } joyaxis[MAX_AXES] = { 0 }; -static byte currentbinding; // add posibility to remap keys, to place it in joykeys[] static qboolean joy_initialized; static CVAR_DEFINE_AUTO( joy_pitch, "100.0", FCVAR_ARCHIVE | FCVAR_FILTERABLE, "joystick pitch sensitivity" ); @@ -60,8 +59,6 @@ static CVAR_DEFINE_AUTO( joy_pitch_deadzone, DEFAULT_JOY_DEADZONE, FCVAR_ARCHIVE static CVAR_DEFINE_AUTO( joy_yaw_deadzone, DEFAULT_JOY_DEADZONE, FCVAR_ARCHIVE | FCVAR_FILTERABLE, "yaw axis deadzone. Value from 0 to 32767" ); static CVAR_DEFINE_AUTO( joy_axis_binding, "sfpyrl", FCVAR_ARCHIVE | FCVAR_FILTERABLE, "axis hardware id to engine inner axis binding, " "s - side, f - forward, y - yaw, p - pitch, r - left trigger, l - right trigger" ); -static CVAR_DEFINE_AUTO( joy_found, "0", FCVAR_READ_ONLY, "is joystick is connected" ); -static CVAR_DEFINE_AUTO( joy_index, "0", FCVAR_READ_ONLY, "current active joystick" ); CVAR_DEFINE_AUTO( joy_enable, "1", FCVAR_ARCHIVE | FCVAR_FILTERABLE, "enable joystick" ); /* @@ -71,7 +68,7 @@ Joy_IsActive */ qboolean Joy_IsActive( void ) { - return joy_found.value && joy_enable.value; + return joy_enable.value; } /* @@ -81,7 +78,7 @@ Joy_HatMotionEvent DPad events ============ */ -void Joy_HatMotionEvent( byte hat, byte value ) +static void Joy_HatMotionEvent( int value ) { struct { @@ -96,9 +93,6 @@ void Joy_HatMotionEvent( byte hat, byte value ) }; int i; - if( !joy_found.value ) - return; - for( i = 0; i < ARRAYSIZE( keys ); i++ ) { if( value & keys[i].mask ) @@ -225,7 +219,7 @@ static void Joy_ProcessStick( const engineAxis_t engineAxis, short value ) val |= Joy_GetHatValueForAxis( JOY_AXIS_SIDE ); val |= Joy_GetHatValueForAxis( JOY_AXIS_FWD ); - Joy_HatMotionEvent( 0, val ); + Joy_HatMotionEvent( val ); } } @@ -236,23 +230,9 @@ Joy_AxisMotionEvent Axis events ============= */ -void Joy_AxisMotionEvent( byte axis, short value ) +void Joy_AxisMotionEvent( engineAxis_t engineAxis, short value ) { - if( !joy_found.value ) - return; - - if( axis >= MAX_AXES ) - { - Con_Reportf( "Only 6 axes is supported\n" ); - return; - } - - Joy_KnownAxisMotionEvent( joyaxesmap[axis], value ); -} - -void Joy_KnownAxisMotionEvent( engineAxis_t engineAxis, short value ) -{ - if( engineAxis == JOY_AXIS_NULL ) + if( engineAxis >= JOY_AXIS_NULL ) return; if( value == joyaxis[engineAxis].val ) @@ -264,70 +244,6 @@ void Joy_KnownAxisMotionEvent( engineAxis_t engineAxis, short value ) Joy_ProcessStick( engineAxis, value ); } -/* -============= -Joy_BallMotionEvent - -Trackball events. UNDONE -============= -*/ -void Joy_BallMotionEvent( byte ball, short xrel, short yrel ) -{ - //if( !joy_found.value ) - // return; -} - -/* -============= -Joy_ButtonEvent - -Button events -============= -*/ -void Joy_ButtonEvent( byte button, byte down ) -{ - if( !joy_found.value ) - return; - - // generic game button code. - if( button > 32 ) - { - int origbutton = button; - button = ( button & 31 ) + K_AUX1; - - Con_Reportf( "Only 32 joybuttons is supported, converting %i button ID to %s\n", origbutton, Key_KeynumToString( button ) ); - } - else button += K_AUX1; - - Key_Event( button, down ); -} - -/* -============= -Joy_RemoveEvent - -Called when joystick is removed. For future expansion -============= -*/ -void Joy_RemoveEvent( void ) -{ - if( joy_found.value ) - Cvar_FullSet( "joy_found", "0", FCVAR_READ_ONLY ); -} - -/* -============= -Joy_RemoveEvent - -Called when joystick is removed. For future expansion -============= -*/ -void Joy_AddEvent( void ) -{ - if( joy_enable.value && !joy_found.value ) - Cvar_FullSet( "joy_found", "1", FCVAR_READ_ONLY ); -} - /* ============= Joy_FinalizeMove @@ -364,14 +280,8 @@ void Joy_FinalizeMove( float *fw, float *side, float *dpitch, float *dyaw ) *fw -= joy_forward.value * (float)joyaxis[JOY_AXIS_FWD ].val/(float)SHRT_MAX; // must be form -1.0 to 1.0 *side += joy_side.value * (float)joyaxis[JOY_AXIS_SIDE].val/(float)SHRT_MAX; -#if !defined(XASH_SDL) - *dpitch += joy_pitch.value * (float)joyaxis[JOY_AXIS_PITCH].val/(float)SHRT_MAX * host.realframetime; // abs axis rotate is frametime related - *dyaw -= joy_yaw.value * (float)joyaxis[JOY_AXIS_YAW ].val/(float)SHRT_MAX * host.realframetime; -#else - // HACKHACK: SDL have inverted look axis. *dpitch -= joy_pitch.value * (float)joyaxis[JOY_AXIS_PITCH].val/(float)SHRT_MAX * host.realframetime; *dyaw += joy_yaw.value * (float)joyaxis[JOY_AXIS_YAW ].val/(float)SHRT_MAX * host.realframetime; -#endif } /* @@ -402,10 +312,6 @@ void Joy_Init( void ) Cvar_RegisterVariable( &joy_yaw_deadzone ); Cvar_RegisterVariable( &joy_axis_binding ); - Cvar_RegisterVariable( &joy_found ); - // we doesn't loaded config.cfg yet, so this cvar is not archive. - // change by +set joy_index in cmdline - Cvar_RegisterVariable( &joy_index ); Cvar_RegisterVariable( &joy_enable ); @@ -417,7 +323,7 @@ void Joy_Init( void ) return; } - Cvar_FullSet( "joy_found", va( "%d", Platform_JoyInit( joy_index.value )), FCVAR_READ_ONLY ); + Platform_JoyInit(); joy_initialized = true; } @@ -431,8 +337,5 @@ Shutdown joystick code */ void Joy_Shutdown( void ) { - if( joy_initialized ) - { - Cvar_FullSet( "joy_found", 0, FCVAR_READ_ONLY ); - } + Platform_JoyShutdown(); } diff --git a/engine/client/input.h b/engine/client/input.h index 0864d887..ae844720 100644 --- a/engine/client/input.h +++ b/engine/client/input.h @@ -105,17 +105,9 @@ typedef enum engineAxis_e } engineAxis_t; qboolean Joy_IsActive( void ); -void Joy_HatMotionEvent( byte hat, byte value ); -void Joy_AxisMotionEvent( byte axis, short value ); -void Joy_KnownAxisMotionEvent( engineAxis_t engineAxis, short value ); -void Joy_BallMotionEvent( byte ball, short xrel, short yrel ); -void Joy_ButtonEvent( byte button, byte down ); -void Joy_AddEvent( void ); -void Joy_RemoveEvent( void ); +void Joy_AxisMotionEvent( engineAxis_t engineAxis, short value ); void Joy_FinalizeMove( float *fw, float *side, float *dpitch, float *dyaw ); void Joy_Init( void ); void Joy_Shutdown( void ); -void Joy_EnableTextInput(qboolean enable, qboolean force); - #endif//INPUT_H diff --git a/engine/client/keys.c b/engine/client/keys.c index cba87e33..0d924d20 100644 --- a/engine/client/keys.c +++ b/engine/client/keys.c @@ -1107,7 +1107,7 @@ static qboolean OSK_KeyEvent( int key, int down ) /* ============= -Joy_EnableTextInput +OSK_EnableTextInput Enables built-in IME ============= diff --git a/engine/common/host.c b/engine/common/host.c index e00f6f66..231ee278 100644 --- a/engine/common/host.c +++ b/engine/common/host.c @@ -201,7 +201,6 @@ static void Sys_PrintUsage( const char *exename ) O("-daemonize ", "run engine as a daemon") #endif #if XASH_SDL == 2 - O("-sdl_joy_old_api ","use SDL legacy joystick API") O("-sdl_renderer ","use alternative SDL_Renderer for software") #endif // XASH_SDL #if XASH_ANDROID && !XASH_SDL diff --git a/engine/platform/dos/in_dos.c b/engine/platform/dos/in_dos.c index 3e2f30cc..0ba1fd5d 100644 --- a/engine/platform/dos/in_dos.c +++ b/engine/platform/dos/in_dos.c @@ -91,11 +91,16 @@ void GAME_EXPORT Platform_SetMousePos(int x, int y) } -int Platform_JoyInit( int numjoy ) +int Platform_JoyInit( void ) { return 0; } +void Platform_JoyShutdown( void ) +{ + +} + void Platform_EnableTextInput( qboolean enable ) { keystate.chars = enable; diff --git a/engine/platform/linux/vid_fbdev.c b/engine/platform/linux/vid_fbdev.c index 143bf605..faab537c 100644 --- a/engine/platform/linux/vid_fbdev.c +++ b/engine/platform/linux/vid_fbdev.c @@ -281,13 +281,19 @@ void GAME_EXPORT Platform_SetMousePos(int x, int y) } -void Platform_Vibrate(float life, char flags) +void Platform_Vibrate( float life, char flags ) { } -int Platform_JoyInit( int numjoy ) + +int Platform_JoyInit( void ) { return 0; } +void Platform_JoyShutdown( void ) +{ + +} + #endif diff --git a/engine/platform/platform.h b/engine/platform/platform.h index b01c8c0a..4024d7ad 100644 --- a/engine/platform/platform.h +++ b/engine/platform/platform.h @@ -209,7 +209,8 @@ void Platform_Vibrate( float life, char flags ); ============================================================================== */ // Gamepad support -int Platform_JoyInit( int numjoy ); // returns number of connected gamepads, negative if error +int Platform_JoyInit( void ); // returns number of connected gamepads, negative if error +void Platform_JoyShutdown( void ); // Text input void Platform_EnableTextInput( qboolean enable ); key_modifier_t Platform_GetKeyModifiers( void ); diff --git a/engine/platform/sdl/events.c b/engine/platform/sdl/events.c index 2bb0c7d6..d31ae60e 100644 --- a/engine/platform/sdl/events.c +++ b/engine/platform/sdl/events.c @@ -88,52 +88,8 @@ GNU General Public License for more details. #define SDL_SCANCODE_PRINTSCREEN SDLK_PRINT #define SDL_SCANCODE_UNKNOWN SDLK_UNKNOWN #define SDL_GetScancodeName( x ) "unknown" -#define SDL_JoystickID Uint8 #endif -static int SDLash_GameControllerButtonMapping[] = -{ -#if XASH_NSWITCH // devkitPro/SDL has inverted Nintendo layout for SDL_GameController - K_B_BUTTON, K_A_BUTTON, K_Y_BUTTON, K_X_BUTTON, -#else - K_A_BUTTON, K_B_BUTTON, K_X_BUTTON, K_Y_BUTTON, -#endif - K_BACK_BUTTON, K_MODE_BUTTON, K_START_BUTTON, - K_LSTICK, K_RSTICK, - K_L1_BUTTON, K_R1_BUTTON, - K_DPAD_UP, K_DPAD_DOWN, K_DPAD_LEFT, K_DPAD_RIGHT, - K_MISC_BUTTON, - K_PADDLE1_BUTTON, K_PADDLE2_BUTTON, K_PADDLE3_BUTTON, K_PADDLE4_BUTTON, - K_TOUCHPAD, -}; - -// Swap axis to follow default axis binding: -// LeftX, LeftY, RightX, RightY, TriggerRight, TriggerLeft -static int SDLash_GameControllerAxisMapping[] = -{ - JOY_AXIS_SIDE, // SDL_CONTROLLER_AXIS_LEFTX, - JOY_AXIS_FWD, // SDL_CONTROLLER_AXIS_LEFTY, - JOY_AXIS_PITCH, // SDL_CONTROLLER_AXIS_RIGHTX, - JOY_AXIS_YAW, // SDL_CONTROLLER_AXIS_RIGHTY, - JOY_AXIS_LT, // SDL_CONTROLLER_AXIS_TRIGGERLEFT, - JOY_AXIS_RT, // SDL_CONTROLLER_AXIS_TRIGGERRIGHT, -}; - -static qboolean SDLash_IsInstanceIDAGameController( SDL_JoystickID joyId ) -{ -#if !SDL_VERSION_ATLEAST( 2, 0, 4 ) - // HACKHACK: if we're not initialized g_joy, then we're probably using gamecontroller api - // so return true - if( !g_joy ) - return true; - return false; -#else - if( SDL_GameControllerFromInstanceID( joyId ) != NULL ) - return true; - return false; -#endif -} - /* ============= SDLash_KeyEvent @@ -408,50 +364,6 @@ static void SDLash_ActiveEvent( int gain ) } } -#if SDL_VERSION_ATLEAST( 2, 0, 0 ) -static size_t num_open_game_controllers = 0; - -static void SDLash_GameController_Add( int index ) -{ - extern convar_t joy_enable; // private to input system - SDL_GameController *controller; - - if( !joy_enable.value ) - return; - - controller = SDL_GameControllerOpen( index ); - if( !controller ) - { - Con_Reportf( "Failed to open SDL GameController %d: %s\n", index, SDL_GetError( ) ); - SDL_ClearError( ); - return; - } -#if SDL_VERSION_ATLEAST( 2, 0, 6 ) - Con_Reportf( "Added controller: %s (%i:%i:%i)\n", - SDL_GameControllerName( controller ), - SDL_GameControllerGetVendor( controller ), - SDL_GameControllerGetProduct( controller ), - SDL_GameControllerGetProductVersion( controller )); -#endif // SDL_VERSION_ATLEAST( 2, 0, 6 ) - - ++num_open_game_controllers; - if( num_open_game_controllers == 1 ) - Joy_AddEvent( ); -} - - -static void SDLash_GameController_Remove( SDL_JoystickID joystick_id ) -{ - Con_Reportf( "Removed controller %i\n", joystick_id ); - - // `Joy_RemoveEvent` sets `joy_found` to `0`. - // We only want to do this when all the game controllers have been removed. - --num_open_game_controllers; - if( num_open_game_controllers == 0 ) - Joy_RemoveEvent( ); -} -#endif - /* ============= SDLash_EventFilter @@ -479,28 +391,6 @@ static void SDLash_EventHandler( SDL_Event *event ) SDLash_KeyEvent( event->key ); break; - /* Joystick events */ - case SDL_JOYAXISMOTION: - if ( !SDLash_IsInstanceIDAGameController( event->jaxis.which )) - Joy_AxisMotionEvent( event->jaxis.axis, event->jaxis.value ); - break; - - case SDL_JOYBALLMOTION: - if ( !SDLash_IsInstanceIDAGameController( event->jball.which )) - Joy_BallMotionEvent( event->jball.ball, event->jball.xrel, event->jball.yrel ); - break; - - case SDL_JOYHATMOTION: - if ( !SDLash_IsInstanceIDAGameController( event->jhat.which )) - Joy_HatMotionEvent( event->jhat.hat, event->jhat.value ); - break; - - case SDL_JOYBUTTONDOWN: - case SDL_JOYBUTTONUP: - if ( !SDLash_IsInstanceIDAGameController( event->jbutton.which )) - Joy_ButtonEvent( event->jbutton.button, event->jbutton.state ); - break; - case SDL_QUIT: Sys_Quit( "caught SDL_QUIT" ); break; @@ -568,46 +458,20 @@ static void SDLash_EventHandler( SDL_Event *event ) SDLash_InputEvent( event->text ); break; - case SDL_JOYDEVICEADDED: - Joy_AddEvent(); - break; - case SDL_JOYDEVICEREMOVED: - Joy_RemoveEvent(); - break; - /* GameController API */ case SDL_CONTROLLERAXISMOTION: - { - if( !Joy_IsActive( )) - break; - - if( event->caxis.axis >= 0 && event->caxis.axis < ARRAYSIZE( SDLash_GameControllerAxisMapping )) - { - Joy_KnownAxisMotionEvent( SDLash_GameControllerAxisMapping[event->caxis.axis], event->caxis.value ); - } - break; - } - case SDL_CONTROLLERBUTTONDOWN: case SDL_CONTROLLERBUTTONUP: - { - if( !Joy_IsActive( )) - break; - - // TODO: Use joyinput funcs, for future multiple gamepads support - if( event->cbutton.button >= 0 && event->cbutton.button < ARRAYSIZE( SDLash_GameControllerButtonMapping )) - { - Key_Event( SDLash_GameControllerButtonMapping[event->cbutton.button], event->cbutton.state ); - } - break; - } - case SDL_CONTROLLERDEVICEADDED: - SDLash_GameController_Add( event->cdevice.which ); - break; - case SDL_CONTROLLERDEVICEREMOVED: - SDLash_GameController_Remove( event->cdevice.which ); + case SDL_CONTROLLERDEVICEREMAPPED: + case SDL_CONTROLLERTOUCHPADDOWN: + case SDL_CONTROLLERTOUCHPADMOTION: + case SDL_CONTROLLERTOUCHPADUP: + case SDL_CONTROLLERSENSORUPDATE: + case SDL_CONTROLLERUPDATECOMPLETE_RESERVED_FOR_SDL3: + case SDL_CONTROLLERSTEAMHANDLEUPDATED: + SDLash_HandleGameControllerEvent( event ); break; case SDL_WINDOWEVENT: diff --git a/engine/platform/sdl/events.h b/engine/platform/sdl/events.h index da709c06..7a9dff06 100644 --- a/engine/platform/sdl/events.h +++ b/engine/platform/sdl/events.h @@ -28,14 +28,16 @@ void GL_InitExtensions( void ); qboolean GL_DeleteContext( void ); void VID_SaveWindowSize( int width, int height, qboolean maximized ); -// joystick events -extern SDL_Joystick *g_joy; - // // in_sdl.c // void SDLash_InitCursors( void ); void SDLash_FreeCursors( void ); +// +// joy_sdl.c +// +void SDLash_HandleGameControllerEvent( SDL_Event *ev ); + #endif // XASH_SDL #endif // KEYWRAPPER_H diff --git a/engine/platform/sdl/in_sdl.c b/engine/platform/sdl/in_sdl.c index 3b0155da..458c3d12 100644 --- a/engine/platform/sdl/in_sdl.c +++ b/engine/platform/sdl/in_sdl.c @@ -1,6 +1,6 @@ /* -vid_sdl.c - SDL input component -Copyright (C) 2018 a1batross +in_sdl.c - SDL input component +Copyright (C) 2018-2025 a1batross This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -12,7 +12,6 @@ but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. */ -#if !XASH_DEDICATED #include #include "common.h" @@ -24,7 +23,6 @@ GNU General Public License for more details. #include "sound.h" #include "vid_common.h" -SDL_Joystick *g_joy = NULL; #if !SDL_VERSION_ATLEAST( 2, 0, 0 ) #define SDL_WarpMouseInWindow( win, x, y ) SDL_WarpMouse( ( x ), ( y ) ) #else @@ -126,20 +124,6 @@ void Platform_SetClipboardText( const char *buffer ) #endif // SDL_VERSION_ATLEAST( 2, 0, 0 ) } -/* -============= -Platform_Vibrate - -============= -*/ -void Platform_Vibrate( float time, char flags ) -{ -#if SDL_VERSION_ATLEAST( 2, 0, 9 ) - if( g_joy ) - SDL_JoystickRumble( g_joy, 0xFFFF, 0xFFFF, time * 1000.0f ); -#endif // SDL_VERSION_ATLEAST( 2, 0, 9 ) -} - #if !XASH_PSVITA /* @@ -157,138 +141,6 @@ void Platform_EnableTextInput( qboolean enable ) #endif // !XASH_PSVITA -/* -============= -SDLash_JoyInit_Old - -============= -*/ -static int SDLash_JoyInit_Old( int numjoy ) -{ - int num; - int i; - - Con_Reportf( "Joystick: SDL\n" ); - - if( SDL_WasInit( SDL_INIT_JOYSTICK ) != SDL_INIT_JOYSTICK && - SDL_InitSubSystem( SDL_INIT_JOYSTICK ) ) - { - Con_Reportf( "Failed to initialize SDL Joysitck: %s\n", SDL_GetError() ); - return 0; - } - - if( g_joy ) - { - SDL_JoystickClose( g_joy ); - } - - num = SDL_NumJoysticks(); - - if( num > 0 ) - Con_Reportf( "%i joysticks found:\n", num ); - else - { - Con_Reportf( "No joystick found.\n" ); - return 0; - } - -#if SDL_VERSION_ATLEAST( 2, 0, 0 ) - for( i = 0; i < num; i++ ) - Con_Reportf( "%i\t: %s\n", i, SDL_JoystickNameForIndex( i ) ); -#endif // SDL_VERSION_ATLEAST( 2, 0, 0 ) - - Con_Reportf( "Pass +set joy_index N to command line, where N is number, to select active joystick\n" ); - - g_joy = SDL_JoystickOpen( numjoy ); - - if( !g_joy ) - { - Con_Reportf( "Failed to select joystick: %s\n", SDL_GetError( ) ); - return 0; - } - -#if SDL_VERSION_ATLEAST( 2, 0, 0 ) - Con_Reportf( "Selected joystick: %s\n" - "\tAxes: %i\n" - "\tHats: %i\n" - "\tButtons: %i\n" - "\tBalls: %i\n", - SDL_JoystickName( g_joy ), SDL_JoystickNumAxes( g_joy ), SDL_JoystickNumHats( g_joy ), - SDL_JoystickNumButtons( g_joy ), SDL_JoystickNumBalls( g_joy ) ); - - SDL_GameControllerEventState( SDL_DISABLE ); -#endif // SDL_VERSION_ATLEAST( 2, 0, 0 ) - SDL_JoystickEventState( SDL_ENABLE ); - - return num; -} - -#if SDL_VERSION_ATLEAST( 2, 0, 0 ) -static void SDLash_GameControllerAddMappings( const char *name ) -{ - fs_offset_t len = 0; - byte *p = FS_LoadFile( name, &len, false ); - - if( !p ) - return; - - if( len > 0 && len < INT32_MAX ) // function accepts int, SDL3 fixes this - { - SDL_RWops *rwops = SDL_RWFromConstMem( p, len ); - SDL_GameControllerAddMappingsFromRW( rwops, true ); - } - - Mem_Free( p ); -} - -/* -============= -SDLash_JoyInit_New - -============= -*/ -static int SDLash_JoyInit_New( int numjoy ) -{ - int count, numJoysticks, i; - - Con_Reportf( "Joystick: SDL GameController API\n" ); - if( SDL_WasInit( SDL_INIT_GAMECONTROLLER ) != SDL_INIT_GAMECONTROLLER && - SDL_InitSubSystem( SDL_INIT_GAMECONTROLLER ) ) - { - Con_Reportf( "Failed to initialize SDL GameController API: %s\n", SDL_GetError() ); - return 0; - } - - SDLash_GameControllerAddMappings( "gamecontrollerdb.txt" ); // shipped in extras.pk3 - SDLash_GameControllerAddMappings( "controllermappings.txt" ); - - count = 0; - numJoysticks = SDL_NumJoysticks(); - for ( i = 0; i < numJoysticks; i++ ) - if( SDL_IsGameController( i ) ) - ++count; - - return count; -} -#endif // SDL_VERSION_ATLEAST( 2, 0, 0 ) - -/* -============= -Platform_JoyInit - -============= -*/ -int Platform_JoyInit( int numjoy ) -{ -#if SDL_VERSION_ATLEAST( 2, 0, 0 ) - // SDL_Joystick is now an old API - // SDL_GameController is preferred - if( !Sys_CheckParm( "-sdl_joy_old_api" ) ) - return SDLash_JoyInit_New(numjoy); -#endif // SDL_VERSION_ATLEAST( 2, 0, 0 ) - return SDLash_JoyInit_Old(numjoy); -} - /* ======================== SDLash_InitCursors @@ -436,5 +288,3 @@ key_modifier_t Platform_GetKeyModifiers( void ) return KeyModifier_None; #endif } - -#endif // XASH_DEDICATED diff --git a/engine/platform/sdl/joy_sdl.c b/engine/platform/sdl/joy_sdl.c new file mode 100644 index 00000000..e48a61cc --- /dev/null +++ b/engine/platform/sdl/joy_sdl.c @@ -0,0 +1,273 @@ +/* +joy_sdl.c - SDL gamepads +Copyright (C) 2018-2025 a1batross + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +*/ +#include + +#if SDL_VERSION_ATLEAST( 2, 0, 0 ) +#include "common.h" +#include "keydefs.h" +#include "input.h" +#include "client.h" +#include "events.h" + +static const int g_button_mapping[] = +{ +#if XASH_NSWITCH // devkitPro/SDL has inverted Nintendo layout for SDL_GameController + K_B_BUTTON, K_A_BUTTON, K_Y_BUTTON, K_X_BUTTON, +#else + K_A_BUTTON, K_B_BUTTON, K_X_BUTTON, K_Y_BUTTON, +#endif + K_BACK_BUTTON, K_MODE_BUTTON, K_START_BUTTON, + K_LSTICK, K_RSTICK, + K_L1_BUTTON, K_R1_BUTTON, + K_DPAD_UP, K_DPAD_DOWN, K_DPAD_LEFT, K_DPAD_RIGHT, + K_MISC_BUTTON, + K_PADDLE1_BUTTON, K_PADDLE2_BUTTON, K_PADDLE3_BUTTON, K_PADDLE4_BUTTON, + K_TOUCHPAD, +}; + +// Swap axis to follow default axis binding: +// LeftX, LeftY, RightX, RightY, TriggerRight, TriggerLeft +static const engineAxis_t g_axis_mapping[] = +{ + JOY_AXIS_SIDE, // SDL_CONTROLLER_AXIS_LEFTX, + JOY_AXIS_FWD, // SDL_CONTROLLER_AXIS_LEFTY, + JOY_AXIS_PITCH, // SDL_CONTROLLER_AXIS_RIGHTX, + JOY_AXIS_YAW, // SDL_CONTROLLER_AXIS_RIGHTY, + JOY_AXIS_LT, // SDL_CONTROLLER_AXIS_TRIGGERLEFT, + JOY_AXIS_RT, // SDL_CONTROLLER_AXIS_TRIGGERRIGHT, +}; + +static SDL_JoystickID g_current_gamepad_id = -1; // used to send rumble to +static SDL_GameController *g_current_gamepad; +static SDL_GameController **g_gamepads; +static size_t g_num_gamepads; + +static void SDLash_GameControllerAddMappings( const char *name ) +{ + fs_offset_t len = 0; + byte *p = FS_LoadFile( name, &len, false ); + + if( !p ) + return; + + if( len > 0 && len < INT32_MAX ) // function accepts int, SDL3 fixes this + { + SDL_RWops *rwops = SDL_RWFromConstMem( p, (int)len ); + SDL_GameControllerAddMappingsFromRW( rwops, true ); + } + + Mem_Free( p ); +} + +static void SDLash_SetActiveGameController( SDL_JoystickID id ) +{ + g_current_gamepad_id = id; + g_current_gamepad = SDL_GameControllerFromInstanceID( id ); +} + +static void SDLash_GameControllerAdded( int device_index ) +{ + SDL_GameController *gc; + SDL_GameController **list; + + gc = SDL_GameControllerOpen( device_index ); + if( !gc ) + { + Con_Printf( S_ERROR "SDL_GameControllerOpen: %s\n", SDL_GetError( )); + return; + } + + list = Mem_Realloc( host.mempool, g_gamepads, sizeof( *list ) * ( g_num_gamepads + 1 )); + list[g_num_gamepads++] = gc; + + g_gamepads = list; + + // set as current device if none other set + if( g_current_gamepad_id < 0 ) + { + SDL_Joystick *joy = SDL_GameControllerGetJoystick( gc ); + + if( joy ) + SDLash_SetActiveGameController( SDL_JoystickInstanceID( joy )); + } +} + +static void SDLash_GameControllerRemoved( SDL_JoystickID id ) +{ + size_t i; + + if( id == g_current_gamepad_id ) + { + g_current_gamepad_id = -1; + g_current_gamepad = NULL; + } + + // now close the device + for( i = 0; i < g_num_gamepads; i++ ) + { + SDL_GameController *gc = g_gamepads[i]; + SDL_Joystick *joy; + + if( !gc ) + continue; + + joy = SDL_GameControllerGetJoystick( gc ); + + if( !joy ) + continue; + + if( SDL_JoystickInstanceID( joy ) == id ) + { + SDL_GameControllerClose( gc ); + g_gamepads[i] = NULL; + } + } +} + +/* +============= +SDLash_JoyInit + +============= +*/ +static int SDLash_JoyInit( void ) +{ + int count, numJoysticks, i; + + Con_Reportf( "Joystick: SDL GameController API\n" ); + if( SDL_WasInit( SDL_INIT_GAMECONTROLLER ) != SDL_INIT_GAMECONTROLLER && + SDL_InitSubSystem( SDL_INIT_GAMECONTROLLER )) + { + Con_Reportf( "Failed to initialize SDL GameController API: %s\n", SDL_GetError() ); + return 0; + } + + SDLash_GameControllerAddMappings( "gamecontrollerdb.txt" ); // shipped in extras.pk3 + SDLash_GameControllerAddMappings( "controllermappings.txt" ); + + count = 0; + numJoysticks = SDL_NumJoysticks(); + for ( i = 0; i < numJoysticks; i++ ) + { + if( SDL_IsGameController( i )) + ++count; + } + + return count; +} + +void SDLash_HandleGameControllerEvent( SDL_Event *ev ) +{ + int x; + + switch( ev->type ) + { + case SDL_CONTROLLERAXISMOTION: + SDLash_SetActiveGameController( ev->caxis.which ); + x = ev->caxis.axis; + if( x >= 0 && x < ARRAYSIZE( g_axis_mapping )) + Joy_AxisMotionEvent( g_axis_mapping[x], ev->caxis.value ); + break; + case SDL_CONTROLLERBUTTONDOWN: + case SDL_CONTROLLERBUTTONUP: + SDLash_SetActiveGameController( ev->cbutton.which ); + x = ev->cbutton.button; + if( x >= 0 && x < ARRAYSIZE( g_button_mapping )) + Key_Event( g_button_mapping[x], ev->cbutton.state ); + break; + case SDL_CONTROLLERDEVICEREMOVED: + SDLash_GameControllerRemoved( ev->cdevice.which ); + break; + case SDL_CONTROLLERDEVICEADDED: + SDLash_GameControllerAdded( ev->cdevice.which ); + break; + } +} + +/* +============= +Platform_Vibrate + +============= +*/ +void Platform_Vibrate( float time, char flags ) +{ +#if SDL_VERSION_ATLEAST( 2, 0, 9 ) + SDL_GameController *gc = g_current_gamepad; + + if( g_current_gamepad_id < 0 || !gc ) + return; + + // a1ba: time is in milliseconds but might be not enough + // to spin up rumble + SDL_GameControllerRumble( gc, 0xFFFF, 0xFFFF, (int)floor( time )); +#endif // SDL_VERSION_ATLEAST( 2, 0, 9 ) +} + +/* +============= +Platform_JoyInit + +============= +*/ +int Platform_JoyInit( void ) +{ + return SDLash_JoyInit(); +} + +/* +============= +Platform_JoyShutdown + +============= +*/ +void Platform_JoyShutdown( void ) +{ + size_t i; + + for( i = 0; i < g_num_gamepads; i++ ) + { + if( !g_gamepads[i] ) + continue; + + SDL_GameControllerClose( g_gamepads[i] ); + g_gamepads[i] = NULL; + } + + Mem_Free( g_gamepads ); + g_gamepads = NULL; + g_num_gamepads = 0; + + g_current_gamepad = NULL; + g_current_gamepad_id = -1; + + SDL_QuitSubSystem( SDL_INIT_GAMECONTROLLER ); +} +#else // SDL_VERSION_ATLEAST( 2, 0, 0 ) +void Platform_Vibrate( float time, char flags ) +{ + +} + +int Platform_JoyInit( void ) +{ + return 0; +} + +void Platform_JoyShutdown( void ) +{ + +} +#endif