Counter Strike : Global Offensive Source Code
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.

566 lines
18 KiB

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