gamepadmap.c 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834
  1. /*
  2. Copyright (C) 1997-2023 Sam Lantinga <[email protected]>
  3. This software is provided 'as-is', without any express or implied
  4. warranty. In no event will the authors be held liable for any damages
  5. arising from the use of this software.
  6. Permission is granted to anyone to use this software for any purpose,
  7. including commercial applications, and to alter it and redistribute it
  8. freely.
  9. */
  10. /* Gamepad mapping generator */
  11. /* Gabriel Jacobo <[email protected]> */
  12. #include <stdio.h>
  13. #include <stdlib.h>
  14. #include <SDL3/SDL.h>
  15. #include <SDL3/SDL_main.h>
  16. #include "testutils.h"
  17. /* Define this for verbose output while mapping gamepads */
  18. #define DEBUG_GAMEPADMAP
  19. #define SCREEN_WIDTH 512
  20. #define SCREEN_HEIGHT 320
  21. enum marker_type
  22. {
  23. MARKER_BUTTON,
  24. MARKER_AXIS,
  25. };
  26. enum
  27. {
  28. SDL_GAMEPAD_BINDING_AXIS_LEFTX_NEGATIVE,
  29. SDL_GAMEPAD_BINDING_AXIS_LEFTX_POSITIVE,
  30. SDL_GAMEPAD_BINDING_AXIS_LEFTY_NEGATIVE,
  31. SDL_GAMEPAD_BINDING_AXIS_LEFTY_POSITIVE,
  32. SDL_GAMEPAD_BINDING_AXIS_RIGHTX_NEGATIVE,
  33. SDL_GAMEPAD_BINDING_AXIS_RIGHTX_POSITIVE,
  34. SDL_GAMEPAD_BINDING_AXIS_RIGHTY_NEGATIVE,
  35. SDL_GAMEPAD_BINDING_AXIS_RIGHTY_POSITIVE,
  36. SDL_GAMEPAD_BINDING_AXIS_TRIGGERLEFT,
  37. SDL_GAMEPAD_BINDING_AXIS_TRIGGERRIGHT,
  38. SDL_GAMEPAD_BINDING_AXIS_MAX,
  39. };
  40. #define BINDING_COUNT (SDL_GAMEPAD_BUTTON_MAX + SDL_GAMEPAD_BINDING_AXIS_MAX)
  41. static struct
  42. {
  43. int x, y;
  44. double angle;
  45. enum marker_type marker;
  46. } s_arrBindingDisplay[] = {
  47. { 387, 167, 0.0, MARKER_BUTTON }, /* SDL_GAMEPAD_BUTTON_A */
  48. { 431, 132, 0.0, MARKER_BUTTON }, /* SDL_GAMEPAD_BUTTON_B */
  49. { 342, 132, 0.0, MARKER_BUTTON }, /* SDL_GAMEPAD_BUTTON_X */
  50. { 389, 101, 0.0, MARKER_BUTTON }, /* SDL_GAMEPAD_BUTTON_Y */
  51. { 174, 132, 0.0, MARKER_BUTTON }, /* SDL_GAMEPAD_BUTTON_BACK */
  52. { 232, 128, 0.0, MARKER_BUTTON }, /* SDL_GAMEPAD_BUTTON_GUIDE */
  53. { 289, 132, 0.0, MARKER_BUTTON }, /* SDL_GAMEPAD_BUTTON_START */
  54. { 75, 154, 0.0, MARKER_BUTTON }, /* SDL_GAMEPAD_BUTTON_LEFT_STICK */
  55. { 305, 230, 0.0, MARKER_BUTTON }, /* SDL_GAMEPAD_BUTTON_RIGHT_STICK */
  56. { 77, 40, 0.0, MARKER_BUTTON }, /* SDL_GAMEPAD_BUTTON_LEFT_SHOULDER */
  57. { 396, 36, 0.0, MARKER_BUTTON }, /* SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER */
  58. { 154, 188, 0.0, MARKER_BUTTON }, /* SDL_GAMEPAD_BUTTON_DPAD_UP */
  59. { 154, 249, 0.0, MARKER_BUTTON }, /* SDL_GAMEPAD_BUTTON_DPAD_DOWN */
  60. { 116, 217, 0.0, MARKER_BUTTON }, /* SDL_GAMEPAD_BUTTON_DPAD_LEFT */
  61. { 186, 217, 0.0, MARKER_BUTTON }, /* SDL_GAMEPAD_BUTTON_DPAD_RIGHT */
  62. { 232, 174, 0.0, MARKER_BUTTON }, /* SDL_GAMEPAD_BUTTON_MISC1 */
  63. { 132, 135, 0.0, MARKER_BUTTON }, /* SDL_GAMEPAD_BUTTON_PADDLE1 */
  64. { 330, 135, 0.0, MARKER_BUTTON }, /* SDL_GAMEPAD_BUTTON_PADDLE2 */
  65. { 132, 175, 0.0, MARKER_BUTTON }, /* SDL_GAMEPAD_BUTTON_PADDLE3 */
  66. { 330, 175, 0.0, MARKER_BUTTON }, /* SDL_GAMEPAD_BUTTON_PADDLE4 */
  67. { 0, 0, 0.0, MARKER_BUTTON }, /* SDL_GAMEPAD_BUTTON_TOUCHPAD */
  68. { 74, 153, 270.0, MARKER_AXIS }, /* SDL_GAMEPAD_BINDING_AXIS_LEFTX_NEGATIVE */
  69. { 74, 153, 90.0, MARKER_AXIS }, /* SDL_GAMEPAD_BINDING_AXIS_LEFTX_POSITIVE */
  70. { 74, 153, 0.0, MARKER_AXIS }, /* SDL_GAMEPAD_BINDING_AXIS_LEFTY_NEGATIVE */
  71. { 74, 153, 180.0, MARKER_AXIS }, /* SDL_GAMEPAD_BINDING_AXIS_LEFTY_POSITIVE */
  72. { 306, 231, 270.0, MARKER_AXIS }, /* SDL_GAMEPAD_BINDING_AXIS_RIGHTX_NEGATIVE */
  73. { 306, 231, 90.0, MARKER_AXIS }, /* SDL_GAMEPAD_BINDING_AXIS_RIGHTX_POSITIVE */
  74. { 306, 231, 0.0, MARKER_AXIS }, /* SDL_GAMEPAD_BINDING_AXIS_RIGHTY_NEGATIVE */
  75. { 306, 231, 180.0, MARKER_AXIS }, /* SDL_GAMEPAD_BINDING_AXIS_RIGHTY_POSITIVE */
  76. { 91, -20, 180.0, MARKER_AXIS }, /* SDL_GAMEPAD_BINDING_AXIS_TRIGGERLEFT */
  77. { 375, -20, 180.0, MARKER_AXIS }, /* SDL_GAMEPAD_BINDING_AXIS_TRIGGERRIGHT */
  78. };
  79. SDL_COMPILE_TIME_ASSERT(s_arrBindingDisplay, SDL_arraysize(s_arrBindingDisplay) == BINDING_COUNT);
  80. static int s_arrBindingOrder[] = {
  81. SDL_GAMEPAD_BUTTON_A,
  82. SDL_GAMEPAD_BUTTON_B,
  83. SDL_GAMEPAD_BUTTON_Y,
  84. SDL_GAMEPAD_BUTTON_X,
  85. SDL_GAMEPAD_BUTTON_MAX + SDL_GAMEPAD_BINDING_AXIS_LEFTX_NEGATIVE,
  86. SDL_GAMEPAD_BUTTON_MAX + SDL_GAMEPAD_BINDING_AXIS_LEFTX_POSITIVE,
  87. SDL_GAMEPAD_BUTTON_MAX + SDL_GAMEPAD_BINDING_AXIS_LEFTY_NEGATIVE,
  88. SDL_GAMEPAD_BUTTON_MAX + SDL_GAMEPAD_BINDING_AXIS_LEFTY_POSITIVE,
  89. SDL_GAMEPAD_BUTTON_LEFT_STICK,
  90. SDL_GAMEPAD_BUTTON_MAX + SDL_GAMEPAD_BINDING_AXIS_RIGHTX_NEGATIVE,
  91. SDL_GAMEPAD_BUTTON_MAX + SDL_GAMEPAD_BINDING_AXIS_RIGHTX_POSITIVE,
  92. SDL_GAMEPAD_BUTTON_MAX + SDL_GAMEPAD_BINDING_AXIS_RIGHTY_NEGATIVE,
  93. SDL_GAMEPAD_BUTTON_MAX + SDL_GAMEPAD_BINDING_AXIS_RIGHTY_POSITIVE,
  94. SDL_GAMEPAD_BUTTON_RIGHT_STICK,
  95. SDL_GAMEPAD_BUTTON_LEFT_SHOULDER,
  96. SDL_GAMEPAD_BUTTON_MAX + SDL_GAMEPAD_BINDING_AXIS_TRIGGERLEFT,
  97. SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER,
  98. SDL_GAMEPAD_BUTTON_MAX + SDL_GAMEPAD_BINDING_AXIS_TRIGGERRIGHT,
  99. SDL_GAMEPAD_BUTTON_DPAD_UP,
  100. SDL_GAMEPAD_BUTTON_DPAD_RIGHT,
  101. SDL_GAMEPAD_BUTTON_DPAD_DOWN,
  102. SDL_GAMEPAD_BUTTON_DPAD_LEFT,
  103. SDL_GAMEPAD_BUTTON_BACK,
  104. SDL_GAMEPAD_BUTTON_GUIDE,
  105. SDL_GAMEPAD_BUTTON_START,
  106. SDL_GAMEPAD_BUTTON_MISC1,
  107. SDL_GAMEPAD_BUTTON_PADDLE1,
  108. SDL_GAMEPAD_BUTTON_PADDLE2,
  109. SDL_GAMEPAD_BUTTON_PADDLE3,
  110. SDL_GAMEPAD_BUTTON_PADDLE4,
  111. SDL_GAMEPAD_BUTTON_TOUCHPAD,
  112. };
  113. SDL_COMPILE_TIME_ASSERT(s_arrBindingOrder, SDL_arraysize(s_arrBindingOrder) == BINDING_COUNT);
  114. typedef struct
  115. {
  116. SDL_GamepadBindingType bindType;
  117. union
  118. {
  119. int button;
  120. struct
  121. {
  122. int axis;
  123. int axis_min;
  124. int axis_max;
  125. } axis;
  126. struct
  127. {
  128. int hat;
  129. int hat_mask;
  130. } hat;
  131. } value;
  132. SDL_bool committed;
  133. } SDL_GameControllerExtendedBind;
  134. static SDL_GameControllerExtendedBind s_arrBindings[BINDING_COUNT];
  135. typedef struct
  136. {
  137. SDL_bool m_bMoving;
  138. int m_nLastValue;
  139. int m_nStartingValue;
  140. int m_nFarthestValue;
  141. } AxisState;
  142. static int s_nNumAxes;
  143. static AxisState *s_arrAxisState;
  144. static int s_iCurrentBinding;
  145. static Uint64 s_unPendingAdvanceTime;
  146. static SDL_bool s_bBindingComplete;
  147. static SDL_Window *window;
  148. static SDL_Renderer *screen;
  149. static SDL_bool done = SDL_FALSE;
  150. static SDL_bool bind_touchpad = SDL_FALSE;
  151. static int
  152. StandardizeAxisValue(int nValue)
  153. {
  154. if (nValue > SDL_JOYSTICK_AXIS_MAX / 2) {
  155. return SDL_JOYSTICK_AXIS_MAX;
  156. } else if (nValue < SDL_JOYSTICK_AXIS_MIN / 2) {
  157. return SDL_JOYSTICK_AXIS_MIN;
  158. } else {
  159. return 0;
  160. }
  161. }
  162. static void
  163. SetCurrentBinding(int iBinding)
  164. {
  165. int iIndex;
  166. SDL_GameControllerExtendedBind *pBinding;
  167. if (iBinding < 0) {
  168. return;
  169. }
  170. if (iBinding == BINDING_COUNT) {
  171. s_bBindingComplete = SDL_TRUE;
  172. return;
  173. }
  174. if (s_arrBindingOrder[iBinding] == -1) {
  175. SetCurrentBinding(iBinding + 1);
  176. return;
  177. }
  178. if (s_arrBindingOrder[iBinding] == SDL_GAMEPAD_BUTTON_TOUCHPAD &&
  179. !bind_touchpad) {
  180. SetCurrentBinding(iBinding + 1);
  181. return;
  182. }
  183. s_iCurrentBinding = iBinding;
  184. pBinding = &s_arrBindings[s_arrBindingOrder[s_iCurrentBinding]];
  185. SDL_zerop(pBinding);
  186. for (iIndex = 0; iIndex < s_nNumAxes; ++iIndex) {
  187. s_arrAxisState[iIndex].m_nFarthestValue = s_arrAxisState[iIndex].m_nStartingValue;
  188. }
  189. s_unPendingAdvanceTime = 0;
  190. }
  191. static SDL_bool
  192. BBindingContainsBinding(const SDL_GameControllerExtendedBind *pBindingA, const SDL_GameControllerExtendedBind *pBindingB)
  193. {
  194. if (pBindingA->bindType != pBindingB->bindType) {
  195. return SDL_FALSE;
  196. }
  197. switch (pBindingA->bindType) {
  198. case SDL_GAMEPAD_BINDTYPE_AXIS:
  199. if (pBindingA->value.axis.axis != pBindingB->value.axis.axis) {
  200. return SDL_FALSE;
  201. }
  202. if (!pBindingA->committed) {
  203. return SDL_FALSE;
  204. }
  205. {
  206. int minA = SDL_min(pBindingA->value.axis.axis_min, pBindingA->value.axis.axis_max);
  207. int maxA = SDL_max(pBindingA->value.axis.axis_min, pBindingA->value.axis.axis_max);
  208. int minB = SDL_min(pBindingB->value.axis.axis_min, pBindingB->value.axis.axis_max);
  209. int maxB = SDL_max(pBindingB->value.axis.axis_min, pBindingB->value.axis.axis_max);
  210. return minA <= minB && maxA >= maxB;
  211. }
  212. /* Not reached */
  213. default:
  214. return SDL_memcmp(pBindingA, pBindingB, sizeof(*pBindingA)) == 0;
  215. }
  216. }
  217. static void
  218. ConfigureBinding(const SDL_GameControllerExtendedBind *pBinding)
  219. {
  220. SDL_GameControllerExtendedBind *pCurrent;
  221. int iIndex;
  222. int iCurrentElement = s_arrBindingOrder[s_iCurrentBinding];
  223. /* Do we already have this binding? */
  224. for (iIndex = 0; iIndex < SDL_arraysize(s_arrBindings); ++iIndex) {
  225. pCurrent = &s_arrBindings[iIndex];
  226. if (BBindingContainsBinding(pCurrent, pBinding)) {
  227. if (iIndex == SDL_GAMEPAD_BUTTON_A && iCurrentElement != SDL_GAMEPAD_BUTTON_B) {
  228. /* Skip to the next binding */
  229. SetCurrentBinding(s_iCurrentBinding + 1);
  230. return;
  231. }
  232. if (iIndex == SDL_GAMEPAD_BUTTON_B) {
  233. /* Go back to the previous binding */
  234. SetCurrentBinding(s_iCurrentBinding - 1);
  235. return;
  236. }
  237. /* Already have this binding, ignore it */
  238. return;
  239. }
  240. }
  241. #ifdef DEBUG_GAMEPADMAP
  242. switch (pBinding->bindType) {
  243. case SDL_GAMEPAD_BINDTYPE_NONE:
  244. break;
  245. case SDL_GAMEPAD_BINDTYPE_BUTTON:
  246. SDL_Log("Configuring button binding for button %d\n", pBinding->value.button);
  247. break;
  248. case SDL_GAMEPAD_BINDTYPE_AXIS:
  249. SDL_Log("Configuring axis binding for axis %d %d/%d committed = %s\n", pBinding->value.axis.axis, pBinding->value.axis.axis_min, pBinding->value.axis.axis_max, pBinding->committed ? "true" : "false");
  250. break;
  251. case SDL_GAMEPAD_BINDTYPE_HAT:
  252. SDL_Log("Configuring hat binding for hat %d %d\n", pBinding->value.hat.hat, pBinding->value.hat.hat_mask);
  253. break;
  254. }
  255. #endif /* DEBUG_GAMEPADMAP */
  256. /* Should the new binding override the existing one? */
  257. pCurrent = &s_arrBindings[iCurrentElement];
  258. if (pCurrent->bindType != SDL_GAMEPAD_BINDTYPE_NONE) {
  259. SDL_bool bNativeDPad, bCurrentDPad;
  260. SDL_bool bNativeAxis, bCurrentAxis;
  261. bNativeDPad = (iCurrentElement == SDL_GAMEPAD_BUTTON_DPAD_UP ||
  262. iCurrentElement == SDL_GAMEPAD_BUTTON_DPAD_DOWN ||
  263. iCurrentElement == SDL_GAMEPAD_BUTTON_DPAD_LEFT ||
  264. iCurrentElement == SDL_GAMEPAD_BUTTON_DPAD_RIGHT);
  265. bCurrentDPad = (pCurrent->bindType == SDL_GAMEPAD_BINDTYPE_HAT);
  266. if (bNativeDPad && bCurrentDPad) {
  267. /* We already have a binding of the type we want, ignore the new one */
  268. return;
  269. }
  270. bNativeAxis = (iCurrentElement >= SDL_GAMEPAD_BUTTON_MAX);
  271. bCurrentAxis = (pCurrent->bindType == SDL_GAMEPAD_BINDTYPE_AXIS);
  272. if (bNativeAxis == bCurrentAxis &&
  273. (pBinding->bindType != SDL_GAMEPAD_BINDTYPE_AXIS ||
  274. pBinding->value.axis.axis != pCurrent->value.axis.axis)) {
  275. /* We already have a binding of the type we want, ignore the new one */
  276. return;
  277. }
  278. }
  279. *pCurrent = *pBinding;
  280. if (pBinding->committed) {
  281. s_unPendingAdvanceTime = SDL_GetTicks();
  282. } else {
  283. s_unPendingAdvanceTime = 0;
  284. }
  285. }
  286. static SDL_bool
  287. BMergeAxisBindings(int iIndex)
  288. {
  289. SDL_GameControllerExtendedBind *pBindingA = &s_arrBindings[iIndex];
  290. SDL_GameControllerExtendedBind *pBindingB = &s_arrBindings[iIndex + 1];
  291. if (pBindingA->bindType == SDL_GAMEPAD_BINDTYPE_AXIS &&
  292. pBindingB->bindType == SDL_GAMEPAD_BINDTYPE_AXIS &&
  293. pBindingA->value.axis.axis == pBindingB->value.axis.axis) {
  294. if (pBindingA->value.axis.axis_min == pBindingB->value.axis.axis_min) {
  295. pBindingA->value.axis.axis_min = pBindingA->value.axis.axis_max;
  296. pBindingA->value.axis.axis_max = pBindingB->value.axis.axis_max;
  297. pBindingB->bindType = SDL_GAMEPAD_BINDTYPE_NONE;
  298. return SDL_TRUE;
  299. }
  300. }
  301. return SDL_FALSE;
  302. }
  303. static void
  304. WatchJoystick(SDL_Joystick *joystick)
  305. {
  306. SDL_Texture *background_front, *background_back, *button, *axis, *marker = NULL;
  307. const char *name = NULL;
  308. SDL_Event event;
  309. SDL_FRect dst;
  310. int texture_w, texture_h;
  311. Uint8 alpha = 200, alpha_step = -1;
  312. Uint64 alpha_ticks = 0;
  313. SDL_JoystickID nJoystickID;
  314. background_front = LoadTexture(screen, "gamepadmap.bmp", SDL_FALSE, NULL, NULL);
  315. background_back = LoadTexture(screen, "gamepadmap_back.bmp", SDL_FALSE, NULL, NULL);
  316. button = LoadTexture(screen, "button.bmp", SDL_TRUE, NULL, NULL);
  317. axis = LoadTexture(screen, "axis.bmp", SDL_TRUE, NULL, NULL);
  318. SDL_RaiseWindow(window);
  319. /* scale for platforms that don't give you the window size you asked for. */
  320. SDL_SetRenderLogicalPresentation(screen, SCREEN_WIDTH, SCREEN_HEIGHT,
  321. SDL_LOGICAL_PRESENTATION_LETTERBOX,
  322. SDL_SCALEMODE_LINEAR);
  323. /* Print info about the joystick we are watching */
  324. name = SDL_GetJoystickName(joystick);
  325. SDL_Log("Watching joystick %" SDL_PRIs32 ": (%s)\n", SDL_GetJoystickInstanceID(joystick),
  326. name ? name : "Unknown Joystick");
  327. SDL_Log("Joystick has %d axes, %d hats, and %d buttons\n",
  328. SDL_GetNumJoystickAxes(joystick), SDL_GetNumJoystickHats(joystick), SDL_GetNumJoystickButtons(joystick));
  329. SDL_Log("\n\n\
  330. ====================================================================================\n\
  331. Press the buttons on your gamepad when indicated\n\
  332. (Your gamepad may look different than the picture)\n\
  333. If you want to correct a mistake, press backspace or the back button on your device\n\
  334. To skip a button, press SPACE or click/touch the screen\n\
  335. To exit, press ESC\n\
  336. ====================================================================================\n");
  337. nJoystickID = SDL_GetJoystickInstanceID(joystick);
  338. s_nNumAxes = SDL_GetNumJoystickAxes(joystick);
  339. s_arrAxisState = (AxisState *)SDL_calloc(s_nNumAxes, sizeof(*s_arrAxisState));
  340. /* Skip any spurious events at start */
  341. while (SDL_PollEvent(&event) > 0) {
  342. }
  343. /* Loop, getting joystick events! */
  344. while (!done && !s_bBindingComplete) {
  345. int iElement = s_arrBindingOrder[s_iCurrentBinding];
  346. switch (s_arrBindingDisplay[iElement].marker) {
  347. case MARKER_AXIS:
  348. marker = axis;
  349. break;
  350. case MARKER_BUTTON:
  351. marker = button;
  352. break;
  353. }
  354. SDL_QueryTexture(marker, NULL, NULL, &texture_w, &texture_h);
  355. dst.x = (float)s_arrBindingDisplay[iElement].x;
  356. dst.y = (float)s_arrBindingDisplay[iElement].y;
  357. dst.w = (float)texture_w;
  358. dst.h = (float)texture_h;
  359. if (SDL_GetTicks() >= (alpha_ticks + 5)) {
  360. alpha_ticks = SDL_GetTicks();
  361. alpha += alpha_step;
  362. if (alpha == 255) {
  363. alpha_step = -1;
  364. }
  365. if (alpha < 128) {
  366. alpha_step = 1;
  367. }
  368. }
  369. SDL_SetRenderDrawColor(screen, 0xFF, 0xFF, 0xFF, SDL_ALPHA_OPAQUE);
  370. SDL_RenderClear(screen);
  371. if (s_arrBindingOrder[s_iCurrentBinding] >= SDL_GAMEPAD_BUTTON_PADDLE1 &&
  372. s_arrBindingOrder[s_iCurrentBinding] <= SDL_GAMEPAD_BUTTON_PADDLE4) {
  373. SDL_RenderTexture(screen, background_back, NULL, NULL);
  374. } else {
  375. SDL_RenderTexture(screen, background_front, NULL, NULL);
  376. }
  377. SDL_SetTextureAlphaMod(marker, alpha);
  378. SDL_SetTextureColorMod(marker, 10, 255, 21);
  379. SDL_RenderTextureRotated(screen, marker, NULL, &dst, s_arrBindingDisplay[iElement].angle, NULL, SDL_FLIP_NONE);
  380. SDL_RenderPresent(screen);
  381. while (SDL_PollEvent(&event) > 0) {
  382. switch (event.type) {
  383. case SDL_EVENT_JOYSTICK_REMOVED:
  384. if (event.jaxis.which == nJoystickID) {
  385. done = SDL_TRUE;
  386. }
  387. break;
  388. case SDL_EVENT_JOYSTICK_AXIS_MOTION:
  389. if (event.jaxis.which == nJoystickID) {
  390. const int MAX_ALLOWED_JITTER = SDL_JOYSTICK_AXIS_MAX / 80; /* ShanWan PS3 gamepad needed 96 */
  391. AxisState *pAxisState = &s_arrAxisState[event.jaxis.axis];
  392. int nValue = event.jaxis.value;
  393. int nCurrentDistance, nFarthestDistance;
  394. if (!pAxisState->m_bMoving) {
  395. Sint16 nInitialValue;
  396. pAxisState->m_bMoving = SDL_GetJoystickAxisInitialState(joystick, event.jaxis.axis, &nInitialValue);
  397. pAxisState->m_nLastValue = nValue;
  398. pAxisState->m_nStartingValue = nInitialValue;
  399. pAxisState->m_nFarthestValue = nInitialValue;
  400. } else if (SDL_abs(nValue - pAxisState->m_nLastValue) <= MAX_ALLOWED_JITTER) {
  401. break;
  402. } else {
  403. pAxisState->m_nLastValue = nValue;
  404. }
  405. nCurrentDistance = SDL_abs(nValue - pAxisState->m_nStartingValue);
  406. nFarthestDistance = SDL_abs(pAxisState->m_nFarthestValue - pAxisState->m_nStartingValue);
  407. if (nCurrentDistance > nFarthestDistance) {
  408. pAxisState->m_nFarthestValue = nValue;
  409. nFarthestDistance = SDL_abs(pAxisState->m_nFarthestValue - pAxisState->m_nStartingValue);
  410. }
  411. #ifdef DEBUG_GAMEPADMAP
  412. SDL_Log("AXIS %d nValue %d nCurrentDistance %d nFarthestDistance %d\n", event.jaxis.axis, nValue, nCurrentDistance, nFarthestDistance);
  413. #endif
  414. if (nFarthestDistance >= 16000) {
  415. /* If we've gone out far enough and started to come back, let's bind this axis */
  416. SDL_bool bCommitBinding = (nCurrentDistance <= 10000) ? SDL_TRUE : SDL_FALSE;
  417. SDL_GameControllerExtendedBind binding;
  418. SDL_zero(binding);
  419. binding.bindType = SDL_GAMEPAD_BINDTYPE_AXIS;
  420. binding.value.axis.axis = event.jaxis.axis;
  421. binding.value.axis.axis_min = StandardizeAxisValue(pAxisState->m_nStartingValue);
  422. binding.value.axis.axis_max = StandardizeAxisValue(pAxisState->m_nFarthestValue);
  423. binding.committed = bCommitBinding;
  424. ConfigureBinding(&binding);
  425. }
  426. }
  427. break;
  428. case SDL_EVENT_JOYSTICK_HAT_MOTION:
  429. if (event.jhat.which == nJoystickID) {
  430. if (event.jhat.value != SDL_HAT_CENTERED) {
  431. SDL_GameControllerExtendedBind binding;
  432. #ifdef DEBUG_GAMEPADMAP
  433. SDL_Log("HAT %d %d\n", event.jhat.hat, event.jhat.value);
  434. #endif
  435. SDL_zero(binding);
  436. binding.bindType = SDL_GAMEPAD_BINDTYPE_HAT;
  437. binding.value.hat.hat = event.jhat.hat;
  438. binding.value.hat.hat_mask = event.jhat.value;
  439. binding.committed = SDL_TRUE;
  440. ConfigureBinding(&binding);
  441. }
  442. }
  443. break;
  444. case SDL_EVENT_JOYSTICK_BUTTON_UP:
  445. if (event.jbutton.which == nJoystickID) {
  446. SDL_GameControllerExtendedBind binding;
  447. #ifdef DEBUG_GAMEPADMAP
  448. SDL_Log("BUTTON %d\n", event.jbutton.button);
  449. #endif
  450. SDL_zero(binding);
  451. binding.bindType = SDL_GAMEPAD_BINDTYPE_BUTTON;
  452. binding.value.button = event.jbutton.button;
  453. binding.committed = SDL_TRUE;
  454. ConfigureBinding(&binding);
  455. }
  456. break;
  457. case SDL_EVENT_FINGER_DOWN:
  458. case SDL_EVENT_MOUSE_BUTTON_DOWN:
  459. /* Skip this step */
  460. SetCurrentBinding(s_iCurrentBinding + 1);
  461. break;
  462. case SDL_EVENT_KEY_DOWN:
  463. if (event.key.keysym.sym == SDLK_BACKSPACE || event.key.keysym.sym == SDLK_AC_BACK) {
  464. SetCurrentBinding(s_iCurrentBinding - 1);
  465. break;
  466. }
  467. if (event.key.keysym.sym == SDLK_SPACE) {
  468. SetCurrentBinding(s_iCurrentBinding + 1);
  469. break;
  470. }
  471. if (event.key.keysym.sym != SDLK_ESCAPE) {
  472. break;
  473. }
  474. SDL_FALLTHROUGH;
  475. case SDL_EVENT_QUIT:
  476. done = SDL_TRUE;
  477. break;
  478. default:
  479. break;
  480. }
  481. }
  482. SDL_Delay(15);
  483. /* Wait 30 ms for joystick events to stop coming in,
  484. in case a gamepad sends multiple events for a single control (e.g. axis and button for trigger)
  485. */
  486. if (s_unPendingAdvanceTime && SDL_GetTicks() - s_unPendingAdvanceTime >= 30) {
  487. SetCurrentBinding(s_iCurrentBinding + 1);
  488. }
  489. }
  490. if (s_bBindingComplete) {
  491. SDL_JoystickGUID guid;
  492. Uint16 crc;
  493. char mapping[1024];
  494. char trimmed_name[128];
  495. char *spot;
  496. int iIndex;
  497. char pszElement[12];
  498. SDL_strlcpy(trimmed_name, name, SDL_arraysize(trimmed_name));
  499. while (SDL_isspace(trimmed_name[0])) {
  500. SDL_memmove(&trimmed_name[0], &trimmed_name[1], SDL_strlen(trimmed_name));
  501. }
  502. while (trimmed_name[0] && SDL_isspace(trimmed_name[SDL_strlen(trimmed_name) - 1])) {
  503. trimmed_name[SDL_strlen(trimmed_name) - 1] = '\0';
  504. }
  505. while ((spot = SDL_strchr(trimmed_name, ',')) != NULL) {
  506. SDL_memmove(spot, spot + 1, SDL_strlen(spot));
  507. }
  508. /* Initialize mapping with GUID and name */
  509. guid = SDL_GetJoystickGUID(joystick);
  510. SDL_GetJoystickGUIDInfo(guid, NULL, NULL, NULL, &crc);
  511. if (crc) {
  512. /* Clear the CRC from the GUID for the mapping */
  513. guid.data[2] = 0;
  514. guid.data[3] = 0;
  515. }
  516. SDL_GetJoystickGUIDString(guid, mapping, SDL_arraysize(mapping));
  517. SDL_strlcat(mapping, ",", SDL_arraysize(mapping));
  518. SDL_strlcat(mapping, trimmed_name, SDL_arraysize(mapping));
  519. SDL_strlcat(mapping, ",", SDL_arraysize(mapping));
  520. SDL_strlcat(mapping, "platform:", SDL_arraysize(mapping));
  521. SDL_strlcat(mapping, SDL_GetPlatform(), SDL_arraysize(mapping));
  522. SDL_strlcat(mapping, ",", SDL_arraysize(mapping));
  523. if (crc) {
  524. char crc_string[5];
  525. SDL_strlcat(mapping, "crc:", SDL_arraysize(mapping));
  526. (void)SDL_snprintf(crc_string, sizeof(crc_string), "%.4x", crc);
  527. SDL_strlcat(mapping, crc_string, SDL_arraysize(mapping));
  528. SDL_strlcat(mapping, ",", SDL_arraysize(mapping));
  529. }
  530. for (iIndex = 0; iIndex < SDL_arraysize(s_arrBindings); ++iIndex) {
  531. SDL_GameControllerExtendedBind *pBinding = &s_arrBindings[iIndex];
  532. if (pBinding->bindType == SDL_GAMEPAD_BINDTYPE_NONE) {
  533. continue;
  534. }
  535. if (iIndex < SDL_GAMEPAD_BUTTON_MAX) {
  536. SDL_GamepadButton eButton = (SDL_GamepadButton)iIndex;
  537. SDL_strlcat(mapping, SDL_GetGamepadStringForButton(eButton), SDL_arraysize(mapping));
  538. } else {
  539. const char *pszAxisName = NULL;
  540. switch (iIndex - SDL_GAMEPAD_BUTTON_MAX) {
  541. case SDL_GAMEPAD_BINDING_AXIS_LEFTX_NEGATIVE:
  542. if (!BMergeAxisBindings(iIndex)) {
  543. SDL_strlcat(mapping, "-", SDL_arraysize(mapping));
  544. }
  545. pszAxisName = SDL_GetGamepadStringForAxis(SDL_GAMEPAD_AXIS_LEFTX);
  546. break;
  547. case SDL_GAMEPAD_BINDING_AXIS_LEFTX_POSITIVE:
  548. SDL_strlcat(mapping, "+", SDL_arraysize(mapping));
  549. pszAxisName = SDL_GetGamepadStringForAxis(SDL_GAMEPAD_AXIS_LEFTX);
  550. break;
  551. case SDL_GAMEPAD_BINDING_AXIS_LEFTY_NEGATIVE:
  552. if (!BMergeAxisBindings(iIndex)) {
  553. SDL_strlcat(mapping, "-", SDL_arraysize(mapping));
  554. }
  555. pszAxisName = SDL_GetGamepadStringForAxis(SDL_GAMEPAD_AXIS_LEFTY);
  556. break;
  557. case SDL_GAMEPAD_BINDING_AXIS_LEFTY_POSITIVE:
  558. SDL_strlcat(mapping, "+", SDL_arraysize(mapping));
  559. pszAxisName = SDL_GetGamepadStringForAxis(SDL_GAMEPAD_AXIS_LEFTY);
  560. break;
  561. case SDL_GAMEPAD_BINDING_AXIS_RIGHTX_NEGATIVE:
  562. if (!BMergeAxisBindings(iIndex)) {
  563. SDL_strlcat(mapping, "-", SDL_arraysize(mapping));
  564. }
  565. pszAxisName = SDL_GetGamepadStringForAxis(SDL_GAMEPAD_AXIS_RIGHTX);
  566. break;
  567. case SDL_GAMEPAD_BINDING_AXIS_RIGHTX_POSITIVE:
  568. SDL_strlcat(mapping, "+", SDL_arraysize(mapping));
  569. pszAxisName = SDL_GetGamepadStringForAxis(SDL_GAMEPAD_AXIS_RIGHTX);
  570. break;
  571. case SDL_GAMEPAD_BINDING_AXIS_RIGHTY_NEGATIVE:
  572. if (!BMergeAxisBindings(iIndex)) {
  573. SDL_strlcat(mapping, "-", SDL_arraysize(mapping));
  574. }
  575. pszAxisName = SDL_GetGamepadStringForAxis(SDL_GAMEPAD_AXIS_RIGHTY);
  576. break;
  577. case SDL_GAMEPAD_BINDING_AXIS_RIGHTY_POSITIVE:
  578. SDL_strlcat(mapping, "+", SDL_arraysize(mapping));
  579. pszAxisName = SDL_GetGamepadStringForAxis(SDL_GAMEPAD_AXIS_RIGHTY);
  580. break;
  581. case SDL_GAMEPAD_BINDING_AXIS_TRIGGERLEFT:
  582. pszAxisName = SDL_GetGamepadStringForAxis(SDL_GAMEPAD_AXIS_LEFT_TRIGGER);
  583. break;
  584. case SDL_GAMEPAD_BINDING_AXIS_TRIGGERRIGHT:
  585. pszAxisName = SDL_GetGamepadStringForAxis(SDL_GAMEPAD_AXIS_RIGHT_TRIGGER);
  586. break;
  587. }
  588. if (pszAxisName) {
  589. SDL_strlcat(mapping, pszAxisName, SDL_arraysize(mapping));
  590. }
  591. }
  592. SDL_strlcat(mapping, ":", SDL_arraysize(mapping));
  593. pszElement[0] = '\0';
  594. switch (pBinding->bindType) {
  595. case SDL_GAMEPAD_BINDTYPE_BUTTON:
  596. (void)SDL_snprintf(pszElement, sizeof(pszElement), "b%d", pBinding->value.button);
  597. break;
  598. case SDL_GAMEPAD_BINDTYPE_AXIS:
  599. if (pBinding->value.axis.axis_min == 0 && pBinding->value.axis.axis_max == SDL_JOYSTICK_AXIS_MIN) {
  600. /* The negative half axis */
  601. (void)SDL_snprintf(pszElement, sizeof(pszElement), "-a%d", pBinding->value.axis.axis);
  602. } else if (pBinding->value.axis.axis_min == 0 && pBinding->value.axis.axis_max == SDL_JOYSTICK_AXIS_MAX) {
  603. /* The positive half axis */
  604. (void)SDL_snprintf(pszElement, sizeof(pszElement), "+a%d", pBinding->value.axis.axis);
  605. } else {
  606. (void)SDL_snprintf(pszElement, sizeof(pszElement), "a%d", pBinding->value.axis.axis);
  607. if (pBinding->value.axis.axis_min > pBinding->value.axis.axis_max) {
  608. /* Invert the axis */
  609. SDL_strlcat(pszElement, "~", SDL_arraysize(pszElement));
  610. }
  611. }
  612. break;
  613. case SDL_GAMEPAD_BINDTYPE_HAT:
  614. (void)SDL_snprintf(pszElement, sizeof(pszElement), "h%d.%d", pBinding->value.hat.hat, pBinding->value.hat.hat_mask);
  615. break;
  616. default:
  617. SDL_assert(!"Unknown bind type");
  618. break;
  619. }
  620. SDL_strlcat(mapping, pszElement, SDL_arraysize(mapping));
  621. SDL_strlcat(mapping, ",", SDL_arraysize(mapping));
  622. }
  623. SDL_Log("Mapping:\n\n%s\n\n", mapping);
  624. /* Print to stdout as well so the user can cat the output somewhere */
  625. printf("%s\n", mapping);
  626. }
  627. SDL_free(s_arrAxisState);
  628. s_arrAxisState = NULL;
  629. SDL_DestroyRenderer(screen);
  630. }
  631. static SDL_bool HasJoysticks(void)
  632. {
  633. int num_joysticks = 0;
  634. SDL_JoystickID *joysticks;
  635. joysticks = SDL_GetJoysticks(&num_joysticks);
  636. if (joysticks) {
  637. SDL_free(joysticks);
  638. }
  639. if (num_joysticks > 0) {
  640. return SDL_TRUE;
  641. } else {
  642. return SDL_FALSE;
  643. }
  644. }
  645. int main(int argc, char *argv[])
  646. {
  647. const char *name;
  648. int i;
  649. int num_joysticks = 0;
  650. SDL_JoystickID *joysticks;
  651. int joystick_index;
  652. SDL_Joystick *joystick = NULL;
  653. SDL_SetHint(SDL_HINT_ACCELEROMETER_AS_JOYSTICK, "0");
  654. /* Enable standard application logging */
  655. SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO);
  656. /* Initialize SDL (Note: video is required to start event loop) */
  657. if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK) < 0) {
  658. SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't initialize SDL: %s\n", SDL_GetError());
  659. exit(1);
  660. }
  661. if (argv[1] && SDL_strcmp(argv[1], "--bind-touchpad") == 0) {
  662. bind_touchpad = SDL_TRUE;
  663. }
  664. /* Create a window to display joystick axis position */
  665. window = SDL_CreateWindow("Game Controller Map", SCREEN_WIDTH, SCREEN_HEIGHT, 0);
  666. if (window == NULL) {
  667. SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create window: %s\n", SDL_GetError());
  668. return 2;
  669. }
  670. screen = SDL_CreateRenderer(window, NULL, 0);
  671. if (screen == NULL) {
  672. SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create renderer: %s\n", SDL_GetError());
  673. return 2;
  674. }
  675. while (!done && !HasJoysticks()) {
  676. SDL_Event event;
  677. while (SDL_PollEvent(&event) > 0) {
  678. switch (event.type) {
  679. case SDL_EVENT_KEY_DOWN:
  680. if (event.key.keysym.sym != SDLK_ESCAPE) {
  681. break;
  682. }
  683. SDL_FALLTHROUGH;
  684. case SDL_EVENT_QUIT:
  685. done = SDL_TRUE;
  686. break;
  687. default:
  688. break;
  689. }
  690. }
  691. SDL_RenderPresent(screen);
  692. }
  693. /* Print information about the joysticks */
  694. joysticks = SDL_GetJoysticks(&num_joysticks);
  695. if (joysticks) {
  696. SDL_Log("There are %d joysticks attached\n", num_joysticks);
  697. for (i = 0; joysticks[i]; ++i) {
  698. SDL_JoystickID instance_id = joysticks[i];
  699. name = SDL_GetJoystickInstanceName(instance_id);
  700. SDL_Log("Joystick %" SDL_PRIu32 ": %s\n", instance_id, name ? name : "Unknown Joystick");
  701. joystick = SDL_OpenJoystick(instance_id);
  702. if (joystick == NULL) {
  703. SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "SDL_OpenJoystick(%" SDL_PRIu32 ") failed: %s\n", instance_id,
  704. SDL_GetError());
  705. } else {
  706. char guid[64];
  707. SDL_GetJoystickGUIDString(SDL_GetJoystickGUID(joystick),
  708. guid, sizeof(guid));
  709. SDL_Log(" axes: %d\n", SDL_GetNumJoystickAxes(joystick));
  710. SDL_Log(" hats: %d\n", SDL_GetNumJoystickHats(joystick));
  711. SDL_Log(" buttons: %d\n", SDL_GetNumJoystickButtons(joystick));
  712. SDL_Log("instance id: %" SDL_PRIu32 "\n", instance_id);
  713. SDL_Log(" guid: %s\n", guid);
  714. SDL_Log(" VID/PID: 0x%.4x/0x%.4x\n", SDL_GetJoystickVendor(joystick), SDL_GetJoystickProduct(joystick));
  715. SDL_CloseJoystick(joystick);
  716. }
  717. }
  718. }
  719. joystick_index = 0;
  720. for (i = 1; i < argc; ++i) {
  721. if (argv[i] && *argv[i] != '-') {
  722. joystick_index = SDL_atoi(argv[i]);
  723. break;
  724. }
  725. }
  726. if (joysticks && joystick_index < num_joysticks) {
  727. joystick = SDL_OpenJoystick(joysticks[joystick_index]);
  728. if (joystick == NULL) {
  729. SDL_Log("Couldn't open joystick %d: %s\n", joystick_index, SDL_GetError());
  730. }
  731. }
  732. if (joystick != NULL) {
  733. WatchJoystick(joystick);
  734. SDL_CloseJoystick(joystick);
  735. }
  736. SDL_free(joysticks);
  737. SDL_DestroyWindow(window);
  738. SDL_QuitSubSystem(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK);
  739. return 0;
  740. }