Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

615 lines
18 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Linux Joystick implementation for inputsystem.dll
  4. //
  5. //===========================================================================//
  6. /* For force feedback testing. */
  7. #include "inputsystem.h"
  8. #include "tier1/convar.h"
  9. #include "tier0/icommandline.h"
  10. #include "SDL.h"
  11. #include "SDL_gamecontroller.h"
  12. #include "SDL_haptic.h"
  13. // NOTE: This has to be the last file included!
  14. #include "tier0/memdbgon.h"
  15. static ButtonCode_t ControllerButtonToButtonCode( SDL_GameControllerButton button );
  16. static AnalogCode_t ControllerAxisToAnalogCode( SDL_GameControllerAxis axis );
  17. static int JoystickSDLWatcher( void *userInfo, SDL_Event *event );
  18. ConVar joy_axisbutton_threshold( "joy_axisbutton_threshold", "0.3", FCVAR_ARCHIVE, "Analog axis range before a button press is registered." );
  19. ConVar joy_axis_deadzone( "joy_axis_deadzone", "0.2", FCVAR_ARCHIVE, "Dead zone near the zero point to not report movement." );
  20. static void joy_active_changed_f( IConVar *var, const char *pOldValue, float flOldValue );
  21. ConVar joy_active( "joy_active", "-1", FCVAR_NONE, "Which of the connected joysticks / gamepads to use (-1 means first found)", &joy_active_changed_f);
  22. static void joy_gamecontroller_config_changed_f( IConVar *var, const char *pOldValue, float flOldValue );
  23. ConVar joy_gamecontroller_config( "joy_gamecontroller_config", "", FCVAR_ARCHIVE, "Game controller mapping (passed to SDL with SDL_HINT_GAMECONTROLLERCONFIG), can also be configured in Steam Big Picture mode.", &joy_gamecontroller_config_changed_f );
  24. void SearchForDevice()
  25. {
  26. int newJoystickId = joy_active.GetInt();
  27. CInputSystem *pInputSystem = (CInputSystem *)g_pInputSystem;
  28. if ( !pInputSystem )
  29. {
  30. return;
  31. }
  32. // -1 means "first available."
  33. if ( newJoystickId < 0 )
  34. {
  35. pInputSystem->JoystickHotplugAdded(0);
  36. return;
  37. }
  38. for ( int device_index = 0; device_index < SDL_NumJoysticks(); ++device_index )
  39. {
  40. SDL_Joystick *joystick = SDL_JoystickOpen(device_index);
  41. if ( joystick == NULL )
  42. {
  43. continue;
  44. }
  45. int joystickId = SDL_JoystickInstanceID(joystick);
  46. SDL_JoystickClose(joystick);
  47. if ( joystickId == newJoystickId )
  48. {
  49. pInputSystem->JoystickHotplugAdded(device_index);
  50. break;
  51. }
  52. }
  53. }
  54. //---------------------------------------------------------------------------------------
  55. // Switch our active joystick to another device
  56. //---------------------------------------------------------------------------------------
  57. void joy_active_changed_f( IConVar *var, const char *pOldValue, float flOldValue )
  58. {
  59. SearchForDevice();
  60. }
  61. //---------------------------------------------------------------------------------------
  62. // Reinitialize the game controller layer when the joy_gamecontroller_config is updated.
  63. //---------------------------------------------------------------------------------------
  64. void joy_gamecontroller_config_changed_f( IConVar *var, const char *pOldValue, float flOldValue )
  65. {
  66. CInputSystem *pInputSystem = (CInputSystem *)g_pInputSystem;
  67. if ( pInputSystem && SDL_WasInit(SDL_INIT_GAMECONTROLLER) )
  68. {
  69. bool oldValuePresent = pOldValue && ( strlen( pOldValue ) > 0 );
  70. bool newValuePresent = ( strlen( joy_gamecontroller_config.GetString() ) > 0 );
  71. if ( !oldValuePresent && !newValuePresent )
  72. {
  73. return;
  74. }
  75. // We need to reinitialize the whole thing (i.e. undo CInputSystem::InitializeJoysticks and then call it again)
  76. // due to SDL_GameController only reading the SDL_HINT_GAMECONTROLLERCONFIG on init.
  77. pInputSystem->ShutdownJoysticks();
  78. pInputSystem->InitializeJoysticks();
  79. }
  80. }
  81. //-----------------------------------------------------------------------------
  82. // Handle the events coming from the GameController SDL subsystem.
  83. //-----------------------------------------------------------------------------
  84. int JoystickSDLWatcher( void *userInfo, SDL_Event *event )
  85. {
  86. CInputSystem *pInputSystem = (CInputSystem *)userInfo;
  87. Assert(pInputSystem != NULL);
  88. Assert(event != NULL);
  89. if ( event == NULL || pInputSystem == NULL )
  90. {
  91. Warning("No input system\n");
  92. return 1;
  93. }
  94. switch ( event->type )
  95. {
  96. case SDL_CONTROLLERAXISMOTION:
  97. case SDL_CONTROLLERBUTTONDOWN:
  98. case SDL_CONTROLLERBUTTONUP:
  99. case SDL_CONTROLLERDEVICEADDED:
  100. case SDL_CONTROLLERDEVICEREMOVED:
  101. break;
  102. default:
  103. return 1;
  104. }
  105. // This is executed on the same thread as SDL_PollEvent, as PollEvent
  106. // updates the joystick subsystem, which then calls SDL_PushEvent for
  107. // the various events below. PushEvent invokes this callback.
  108. // SDL_PollEvent is called in PumpWindowsMessageLoop which is coming
  109. // from PollInputState_Linux, so there's no worry about calling
  110. // PostEvent (which doesn't seem to be thread safe) from other threads.
  111. Assert(ThreadInMainThread());
  112. switch ( event->type )
  113. {
  114. case SDL_CONTROLLERAXISMOTION:
  115. {
  116. pInputSystem->JoystickAxisMotion(event->caxis.which, event->caxis.axis, event->caxis.value);
  117. break;
  118. }
  119. case SDL_CONTROLLERBUTTONDOWN:
  120. pInputSystem->JoystickButtonPress(event->cbutton.which, event->cbutton.button);
  121. break;
  122. case SDL_CONTROLLERBUTTONUP:
  123. pInputSystem->JoystickButtonRelease(event->cbutton.which, event->cbutton.button);
  124. break;
  125. case SDL_CONTROLLERDEVICEADDED:
  126. pInputSystem->JoystickHotplugAdded(event->cdevice.which);
  127. break;
  128. case SDL_CONTROLLERDEVICEREMOVED:
  129. pInputSystem->JoystickHotplugRemoved(event->cdevice.which);
  130. SearchForDevice();
  131. break;
  132. }
  133. return 1;
  134. }
  135. //-----------------------------------------------------------------------------
  136. // Initialize all joysticks
  137. //-----------------------------------------------------------------------------
  138. void CInputSystem::InitializeJoysticks( void )
  139. {
  140. if ( m_bJoystickInitialized )
  141. {
  142. ShutdownJoysticks();
  143. }
  144. // assume no joystick
  145. m_nJoystickCount = 0;
  146. memset( m_pJoystickInfo, 0, sizeof( m_pJoystickInfo ) );
  147. for ( int i = 0; i < MAX_JOYSTICKS; ++i )
  148. {
  149. m_pJoystickInfo[ i ].m_nDeviceId = -1;
  150. }
  151. // abort startup if user requests no joystick
  152. if ( CommandLine()->FindParm("-nojoy") ) return;
  153. const char *controllerConfig = joy_gamecontroller_config.GetString();
  154. if ( strlen(controllerConfig) > 0 )
  155. {
  156. DevMsg("Passing joy_gamecontroller_config to SDL ('%s').\n", controllerConfig);
  157. // We need to pass this hint to SDL *before* we init the gamecontroller subsystem, otherwise it gets ignored.
  158. SDL_SetHint(SDL_HINT_GAMECONTROLLERCONFIG, controllerConfig);
  159. }
  160. if ( SDL_InitSubSystem( SDL_INIT_GAMECONTROLLER | SDL_INIT_HAPTIC ) == -1 )
  161. {
  162. Warning("Joystick init failed -- SDL_Init(SDL_INIT_GAMECONTROLLER|SDL_INIT_HAPTIC) failed: %s.\n", SDL_GetError());
  163. return;
  164. }
  165. m_bJoystickInitialized = true;
  166. SDL_AddEventWatch(JoystickSDLWatcher, this);
  167. const int totalSticks = SDL_NumJoysticks();
  168. for ( int i = 0; i < totalSticks; i++ )
  169. {
  170. if ( SDL_IsGameController(i) )
  171. {
  172. JoystickHotplugAdded(i);
  173. }
  174. else
  175. {
  176. SDL_JoystickGUID joyGUID = SDL_JoystickGetDeviceGUID(i);
  177. char szGUID[sizeof(joyGUID.data)*2 + 1];
  178. SDL_JoystickGetGUIDString(joyGUID, szGUID, sizeof(szGUID));
  179. Msg("Found joystick '%s' (%s), but no recognized controller configuration for it.\n", SDL_JoystickNameForIndex(i), szGUID);
  180. }
  181. }
  182. if ( totalSticks < 1 )
  183. {
  184. Msg("Did not detect any valid joysticks.\n");
  185. }
  186. }
  187. void CInputSystem::ShutdownJoysticks()
  188. {
  189. if ( !m_bJoystickInitialized )
  190. {
  191. return;
  192. }
  193. SDL_DelEventWatch( JoystickSDLWatcher, this );
  194. if ( m_pJoystickInfo[ 0 ].m_pDevice != NULL )
  195. {
  196. JoystickHotplugRemoved( m_pJoystickInfo[ 0 ].m_nDeviceId );
  197. }
  198. SDL_QuitSubSystem( SDL_INIT_GAMECONTROLLER | SDL_INIT_HAPTIC );
  199. m_bJoystickInitialized = false;
  200. }
  201. // Update the joy_xcontroller_found convar to force CInput::JoyStickMove to re-exec 360controller-linux.cfg
  202. static void SetJoyXControllerFound( bool found )
  203. {
  204. static ConVarRef xcontrollerVar( "joy_xcontroller_found" );
  205. static ConVarRef joystickVar( "joystick" );
  206. if ( xcontrollerVar.IsValid() )
  207. {
  208. xcontrollerVar.SetValue(found);
  209. }
  210. if ( found && joystickVar.IsValid() )
  211. {
  212. joystickVar.SetValue(true);
  213. }
  214. }
  215. void CInputSystem::JoystickHotplugAdded( int joystickIndex )
  216. {
  217. // SDL_IsGameController doesn't bounds check its inputs.
  218. if ( joystickIndex < 0 || joystickIndex >= SDL_NumJoysticks() )
  219. {
  220. return;
  221. }
  222. if ( !SDL_IsGameController(joystickIndex) )
  223. {
  224. Warning("Joystick is not recognized by the game controller system. You can configure the controller in Steam Big Picture mode.\n");
  225. return;
  226. }
  227. SDL_Joystick *joystick = SDL_JoystickOpen(joystickIndex);
  228. if ( joystick == NULL )
  229. {
  230. Warning("Could not open joystick %i: %s", joystickIndex, SDL_GetError());
  231. return;
  232. }
  233. int joystickId = SDL_JoystickInstanceID(joystick);
  234. SDL_JoystickClose(joystick);
  235. int activeJoystick = joy_active.GetInt();
  236. JoystickInfo_t& info = m_pJoystickInfo[ 0 ];
  237. if ( activeJoystick < 0 )
  238. {
  239. // Only opportunistically open devices if we don't have one open already.
  240. if ( info.m_nDeviceId != -1 )
  241. {
  242. Msg("Detected supported joystick #%i '%s'. Currently active joystick is #%i.\n", joystickId, SDL_JoystickNameForIndex(joystickIndex), info.m_nDeviceId);
  243. return;
  244. }
  245. }
  246. else if ( activeJoystick != joystickId )
  247. {
  248. Msg("Detected supported joystick #%i '%s'. Currently active joystick is #%i.\n", joystickId, SDL_JoystickNameForIndex(joystickIndex), activeJoystick);
  249. return;
  250. }
  251. if ( info.m_nDeviceId != -1 )
  252. {
  253. // Don't try to open the device we already have open.
  254. if ( info.m_nDeviceId == joystickId )
  255. {
  256. return;
  257. }
  258. DevMsg("Joystick #%i already initialized, removing it first.\n", info.m_nDeviceId);
  259. JoystickHotplugRemoved(info.m_nDeviceId);
  260. }
  261. Msg("Initializing joystick #%i and making it active.\n", joystickId);
  262. SDL_GameController *controller = SDL_GameControllerOpen(joystickIndex);
  263. if ( controller == NULL )
  264. {
  265. Warning("Failed to open joystick %i: %s\n", joystickId, SDL_GetError());
  266. return;
  267. }
  268. // XXX: This will fail if this is a *real* hotplug event (and not coming from the initial InitializeJoysticks call).
  269. // That's because the SDL haptic subsystem currently doesn't do hotplugging. Everything but haptics will work fine.
  270. SDL_Haptic *haptic = SDL_HapticOpenFromJoystick(SDL_GameControllerGetJoystick(controller));
  271. if ( haptic == NULL || SDL_HapticRumbleInit(haptic) != 0 )
  272. {
  273. Warning("Unable to initialize rumble for joystick #%i: %s\n", joystickId, SDL_GetError());
  274. haptic = NULL;
  275. }
  276. info.m_pDevice = controller;
  277. info.m_pHaptic = haptic;
  278. info.m_nDeviceId = SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(controller));
  279. info.m_nButtonCount = SDL_CONTROLLER_BUTTON_MAX;
  280. info.m_bRumbleEnabled = false;
  281. SetJoyXControllerFound(true);
  282. EnableJoystickInput(0, true);
  283. m_nJoystickCount = 1;
  284. m_bXController = true;
  285. // We reset joy_active to -1 because joystick ids are never reused - until you restart.
  286. // Setting it to -1 means that you get expected hotplugging behavior if you disconnect the current joystick.
  287. joy_active.SetValue(-1);
  288. }
  289. void CInputSystem::JoystickHotplugRemoved( int joystickId )
  290. {
  291. JoystickInfo_t& info = m_pJoystickInfo[ 0 ];
  292. if ( info.m_nDeviceId != joystickId )
  293. {
  294. DevMsg("Ignoring hotplug remove for #%i, active joystick is #%i.\n", joystickId, info.m_nDeviceId);
  295. return;
  296. }
  297. if ( info.m_pDevice == NULL )
  298. {
  299. info.m_nDeviceId = -1;
  300. DevMsg("Got hotplug remove event for removed joystick #%i, ignoring.\n", joystickId);
  301. return;
  302. }
  303. m_nJoystickCount = 0;
  304. m_bXController = false;
  305. EnableJoystickInput(0, false);
  306. SetJoyXControllerFound(false);
  307. SDL_HapticClose((SDL_Haptic *)info.m_pHaptic);
  308. SDL_GameControllerClose((SDL_GameController *)info.m_pDevice);
  309. info.m_pHaptic = NULL;
  310. info.m_pDevice = NULL;
  311. info.m_nButtonCount = 0;
  312. info.m_nDeviceId = -1;
  313. info.m_bRumbleEnabled = false;
  314. Msg("Joystick %i removed.\n", joystickId);
  315. }
  316. void CInputSystem::JoystickButtonPress( int joystickId, int button )
  317. {
  318. JoystickInfo_t& info = m_pJoystickInfo[ 0 ];
  319. if ( info.m_nDeviceId != joystickId )
  320. {
  321. Warning("Not active device input system (%i x %i)\n", info.m_nDeviceId, joystickId);
  322. return;
  323. }
  324. ButtonCode_t buttonCode = ControllerButtonToButtonCode((SDL_GameControllerButton)button);
  325. PostButtonPressedEvent(IE_ButtonPressed, m_nLastSampleTick, buttonCode, buttonCode);
  326. }
  327. void CInputSystem::JoystickButtonRelease( int joystickId, int button )
  328. {
  329. JoystickInfo_t& info = m_pJoystickInfo[ 0 ];
  330. if ( info.m_nDeviceId != joystickId )
  331. {
  332. return;
  333. }
  334. ButtonCode_t buttonCode = ControllerButtonToButtonCode((SDL_GameControllerButton)button);
  335. PostButtonReleasedEvent(IE_ButtonReleased, m_nLastSampleTick, buttonCode, buttonCode);
  336. }
  337. void CInputSystem::JoystickAxisMotion( int joystickId, int axis, int value )
  338. {
  339. JoystickInfo_t& info = m_pJoystickInfo[ 0 ];
  340. if ( info.m_nDeviceId != joystickId )
  341. {
  342. return;
  343. }
  344. AnalogCode_t code = ControllerAxisToAnalogCode((SDL_GameControllerAxis)axis);
  345. if ( code == ANALOG_CODE_INVALID )
  346. {
  347. Warning("Invalid code for axis %i\n", axis);
  348. return;
  349. }
  350. ButtonCode_t buttonCode = BUTTON_CODE_NONE;
  351. switch ( axis )
  352. {
  353. case SDL_CONTROLLER_AXIS_TRIGGERRIGHT:
  354. buttonCode = KEY_XBUTTON_RTRIGGER;
  355. break;
  356. case SDL_CONTROLLER_AXIS_TRIGGERLEFT:
  357. buttonCode = KEY_XBUTTON_LTRIGGER;
  358. break;
  359. }
  360. if ( buttonCode != BUTTON_CODE_NONE )
  361. {
  362. int pressThreshold = joy_axisbutton_threshold.GetFloat() * 32767;
  363. int keyIndex = buttonCode - KEY_XBUTTON_LTRIGGER;
  364. Assert( keyIndex < ARRAYSIZE( m_appXKeys[0] ) && keyIndex >= 0 );
  365. appKey_t &key = m_appXKeys[0][keyIndex];
  366. if ( value > pressThreshold )
  367. {
  368. if ( key.repeats < 1 )
  369. {
  370. PostButtonPressedEvent( IE_ButtonPressed, m_nLastSampleTick, buttonCode, buttonCode );
  371. }
  372. key.repeats++;
  373. }
  374. else
  375. {
  376. PostButtonReleasedEvent( IE_ButtonReleased, m_nLastSampleTick, buttonCode, buttonCode );
  377. key.repeats = 0;
  378. }
  379. }
  380. int minValue = joy_axis_deadzone.GetFloat() * 32767;
  381. if ( abs(value) < minValue )
  382. {
  383. value = 0;
  384. }
  385. InputState_t& state = m_InputState[ m_bIsPolling ];
  386. state.m_pAnalogDelta[ code ] = value - state.m_pAnalogValue[ code ];
  387. state.m_pAnalogValue[ code ] = value;
  388. if ( state.m_pAnalogDelta[ code ] != 0 )
  389. {
  390. PostEvent(IE_AnalogValueChanged, m_nLastSampleTick, code, value, 0);
  391. }
  392. }
  393. //-----------------------------------------------------------------------------
  394. // Process the event
  395. //-----------------------------------------------------------------------------
  396. void CInputSystem::JoystickButtonEvent( ButtonCode_t button, int sample )
  397. {
  398. // Not used - we post button events from JoystickButtonPress/Release.
  399. }
  400. //-----------------------------------------------------------------------------
  401. // Update the joystick button state
  402. //-----------------------------------------------------------------------------
  403. void CInputSystem::UpdateJoystickButtonState( int nJoystick )
  404. {
  405. // We don't sample - we get events posted by SDL_GameController in JoystickSDLWatcher
  406. }
  407. //-----------------------------------------------------------------------------
  408. // Update the joystick POV control
  409. //-----------------------------------------------------------------------------
  410. void CInputSystem::UpdateJoystickPOVControl( int nJoystick )
  411. {
  412. // SDL GameController does not support joystick POV. Should we poll?
  413. }
  414. //-----------------------------------------------------------------------------
  415. // Purpose: Sample the joystick
  416. //-----------------------------------------------------------------------------
  417. void CInputSystem::PollJoystick( void )
  418. {
  419. // We only pump the SDL event loop if we're not an SDL app, since otherwise PollInputState_Platform calls into CSDLMgr to pump it.
  420. // Our state updates happen in events posted by SDL_GameController in JoystickSDLWatcher, so the loop is empty.
  421. #if !defined( USE_SDL )
  422. SDL_Event event;
  423. int nEventsProcessed = 0;
  424. SDL_PumpEvents();
  425. while ( SDL_PollEvent( &event ) && nEventsProcessed < 100 )
  426. {
  427. nEventsProcessed++;
  428. }
  429. #endif
  430. }
  431. void CInputSystem::SetXDeviceRumble( float fLeftMotor, float fRightMotor, int userId )
  432. {
  433. JoystickInfo_t& info = m_pJoystickInfo[ 0 ];
  434. if ( info.m_nDeviceId < 0 || info.m_pHaptic == NULL )
  435. {
  436. return;
  437. }
  438. float strength = (fLeftMotor + fRightMotor) / 2.f;
  439. static ConVarRef joystickVar( "joystick" );
  440. // 0f means "stop".
  441. bool shouldStop = ( strength < 0.01f );
  442. // If they've disabled the gamecontroller in settings, never rumble.
  443. if ( !joystickVar.IsValid() || !joystickVar.GetBool() )
  444. {
  445. shouldStop = true;
  446. }
  447. if ( shouldStop )
  448. {
  449. if ( info.m_bRumbleEnabled )
  450. {
  451. SDL_HapticRumbleStop( (SDL_Haptic *)info.m_pHaptic );
  452. info.m_bRumbleEnabled = false;
  453. info.m_fCurrentRumble = 0.0f;
  454. }
  455. return;
  456. }
  457. // If there's little change, then don't change the rumble strength.
  458. if ( info.m_bRumbleEnabled && abs(info.m_fCurrentRumble - strength) < 0.01f )
  459. {
  460. return;
  461. }
  462. info.m_bRumbleEnabled = true;
  463. info.m_fCurrentRumble = strength;
  464. if ( SDL_HapticRumblePlay((SDL_Haptic *)info.m_pHaptic, strength, SDL_HAPTIC_INFINITY) != 0 )
  465. {
  466. Warning("Couldn't play rumble (strength %.1f): %s\n", strength, SDL_GetError());
  467. }
  468. }
  469. ButtonCode_t ControllerButtonToButtonCode( SDL_GameControllerButton button )
  470. {
  471. switch ( button )
  472. {
  473. case SDL_CONTROLLER_BUTTON_A: // KEY_XBUTTON_A
  474. case SDL_CONTROLLER_BUTTON_B: // KEY_XBUTTON_B
  475. case SDL_CONTROLLER_BUTTON_X: // KEY_XBUTTON_X
  476. case SDL_CONTROLLER_BUTTON_Y: // KEY_XBUTTON_Y
  477. return JOYSTICK_BUTTON(0, button);
  478. case SDL_CONTROLLER_BUTTON_BACK:
  479. return KEY_XBUTTON_BACK;
  480. case SDL_CONTROLLER_BUTTON_START:
  481. return KEY_XBUTTON_START;
  482. case SDL_CONTROLLER_BUTTON_GUIDE:
  483. return KEY_XBUTTON_BACK; // XXX: How are we supposed to handle this? Steam overlay etc.
  484. case SDL_CONTROLLER_BUTTON_LEFTSTICK:
  485. return KEY_XBUTTON_STICK1;
  486. case SDL_CONTROLLER_BUTTON_RIGHTSTICK:
  487. return KEY_XBUTTON_STICK2;
  488. case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
  489. return KEY_XBUTTON_LEFT_SHOULDER;
  490. case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
  491. return KEY_XBUTTON_RIGHT_SHOULDER;
  492. case SDL_CONTROLLER_BUTTON_DPAD_UP:
  493. return KEY_XBUTTON_UP;
  494. case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
  495. return KEY_XBUTTON_DOWN;
  496. case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
  497. return KEY_XBUTTON_LEFT;
  498. case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
  499. return KEY_XBUTTON_RIGHT;
  500. }
  501. return BUTTON_CODE_NONE;
  502. }
  503. AnalogCode_t ControllerAxisToAnalogCode( SDL_GameControllerAxis axis )
  504. {
  505. switch ( axis )
  506. {
  507. case SDL_CONTROLLER_AXIS_LEFTX:
  508. return JOYSTICK_AXIS(0, JOY_AXIS_X);
  509. case SDL_CONTROLLER_AXIS_LEFTY:
  510. return JOYSTICK_AXIS(0, JOY_AXIS_Y);
  511. case SDL_CONTROLLER_AXIS_RIGHTX:
  512. return JOYSTICK_AXIS(0, JOY_AXIS_U);
  513. case SDL_CONTROLLER_AXIS_RIGHTY:
  514. return JOYSTICK_AXIS(0, JOY_AXIS_R);
  515. case SDL_CONTROLLER_AXIS_TRIGGERRIGHT:
  516. case SDL_CONTROLLER_AXIS_TRIGGERLEFT:
  517. return JOYSTICK_AXIS(0, JOY_AXIS_Z);
  518. }
  519. return ANALOG_CODE_INVALID;
  520. }