1
0

gamepadutils.c 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878
  1. /*
  2. Copyright (C) 1997-2023 Sam Lantinga <slouken@libsdl.org>
  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. #include <SDL3/SDL.h>
  11. #include <SDL3/SDL_test_font.h>
  12. #include "gamepadutils.h"
  13. #include "gamepad_front.h"
  14. #include "gamepad_back.h"
  15. #include "gamepad_touchpad.h"
  16. #include "gamepad_button.h"
  17. #include "gamepad_button_small.h"
  18. #include "gamepad_axis.h"
  19. #include "gamepad_axis_arrow.h"
  20. /* This is indexed by SDL_GamepadButton. */
  21. static const struct
  22. {
  23. int x;
  24. int y;
  25. } button_positions[] = {
  26. { 412, 192 }, /* SDL_GAMEPAD_BUTTON_A */
  27. { 456, 157 }, /* SDL_GAMEPAD_BUTTON_B */
  28. { 367, 157 }, /* SDL_GAMEPAD_BUTTON_X */
  29. { 414, 126 }, /* SDL_GAMEPAD_BUTTON_Y */
  30. { 199, 157 }, /* SDL_GAMEPAD_BUTTON_BACK */
  31. { 257, 153 }, /* SDL_GAMEPAD_BUTTON_GUIDE */
  32. { 314, 157 }, /* SDL_GAMEPAD_BUTTON_START */
  33. { 100, 179 }, /* SDL_GAMEPAD_BUTTON_LEFT_STICK */
  34. { 330, 255 }, /* SDL_GAMEPAD_BUTTON_RIGHT_STICK */
  35. { 102, 65 }, /* SDL_GAMEPAD_BUTTON_LEFT_SHOULDER */
  36. { 421, 61 }, /* SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER */
  37. { 179, 213 }, /* SDL_GAMEPAD_BUTTON_DPAD_UP */
  38. { 179, 274 }, /* SDL_GAMEPAD_BUTTON_DPAD_DOWN */
  39. { 141, 242 }, /* SDL_GAMEPAD_BUTTON_DPAD_LEFT */
  40. { 211, 242 }, /* SDL_GAMEPAD_BUTTON_DPAD_RIGHT */
  41. { 257, 199 }, /* SDL_GAMEPAD_BUTTON_MISC1 */
  42. { 157, 160 }, /* SDL_GAMEPAD_BUTTON_PADDLE1 */
  43. { 355, 160 }, /* SDL_GAMEPAD_BUTTON_PADDLE2 */
  44. { 157, 200 }, /* SDL_GAMEPAD_BUTTON_PADDLE3 */
  45. { 355, 200 }, /* SDL_GAMEPAD_BUTTON_PADDLE4 */
  46. };
  47. /* This is indexed by SDL_GamepadAxis. */
  48. static const struct
  49. {
  50. int x;
  51. int y;
  52. double angle;
  53. } axis_positions[] = {
  54. { 99, 178, 270.0 }, /* LEFTX */
  55. { 99, 178, 0.0 }, /* LEFTY */
  56. { 331, 256, 270.0 }, /* RIGHTX */
  57. { 331, 256, 0.0 }, /* RIGHTY */
  58. { 116, 5, 0.0 }, /* TRIGGERLEFT */
  59. { 400, 5, 0.0 }, /* TRIGGERRIGHT */
  60. };
  61. static SDL_Rect touchpad_area = {
  62. 148, 20, 216, 118
  63. };
  64. typedef struct
  65. {
  66. Uint8 state;
  67. float x;
  68. float y;
  69. float pressure;
  70. } GamepadTouchpadFinger;
  71. struct GamepadImage
  72. {
  73. SDL_Renderer *renderer;
  74. SDL_Texture *front_texture;
  75. SDL_Texture *back_texture;
  76. SDL_Texture *touchpad_texture;
  77. SDL_Texture *button_texture;
  78. SDL_Texture *axis_texture;
  79. int gamepad_width;
  80. int gamepad_height;
  81. int touchpad_width;
  82. int touchpad_height;
  83. int button_width;
  84. int button_height;
  85. int axis_width;
  86. int axis_height;
  87. int x;
  88. int y;
  89. SDL_bool showing_front;
  90. SDL_bool showing_touchpad;
  91. SDL_bool buttons[SDL_GAMEPAD_BUTTON_MAX];
  92. int axes[SDL_GAMEPAD_AXIS_MAX];
  93. int num_fingers;
  94. GamepadTouchpadFinger *fingers;
  95. };
  96. static SDL_Texture *CreateTexture(SDL_Renderer *renderer, unsigned char *data, unsigned int len)
  97. {
  98. SDL_Texture *texture = NULL;
  99. SDL_Surface *surface;
  100. SDL_RWops *src = SDL_RWFromConstMem(data, len);
  101. if (src) {
  102. surface = SDL_LoadBMP_RW(src, SDL_TRUE);
  103. if (surface) {
  104. texture = SDL_CreateTextureFromSurface(renderer, surface);
  105. SDL_DestroySurface(surface);
  106. }
  107. }
  108. return texture;
  109. }
  110. GamepadImage *CreateGamepadImage(SDL_Renderer *renderer)
  111. {
  112. GamepadImage *ctx = SDL_calloc(1, sizeof(*ctx));
  113. if (ctx) {
  114. ctx->renderer = renderer;
  115. ctx->front_texture = CreateTexture(renderer, gamepad_front_bmp, gamepad_front_bmp_len);
  116. ctx->back_texture = CreateTexture(renderer, gamepad_back_bmp, gamepad_back_bmp_len);
  117. SDL_QueryTexture(ctx->front_texture, NULL, NULL, &ctx->gamepad_width, &ctx->gamepad_height);
  118. ctx->touchpad_texture = CreateTexture(renderer, gamepad_touchpad_bmp, gamepad_touchpad_bmp_len);
  119. SDL_QueryTexture(ctx->touchpad_texture, NULL, NULL, &ctx->touchpad_width, &ctx->touchpad_height);
  120. ctx->button_texture = CreateTexture(renderer, gamepad_button_bmp, gamepad_button_bmp_len);
  121. SDL_QueryTexture(ctx->button_texture, NULL, NULL, &ctx->button_width, &ctx->button_height);
  122. SDL_SetTextureColorMod(ctx->button_texture, 10, 255, 21);
  123. ctx->axis_texture = CreateTexture(renderer, gamepad_axis_bmp, gamepad_axis_bmp_len);
  124. SDL_QueryTexture(ctx->axis_texture, NULL, NULL, &ctx->axis_width, &ctx->axis_height);
  125. SDL_SetTextureColorMod(ctx->axis_texture, 10, 255, 21);
  126. ctx->showing_front = SDL_TRUE;
  127. }
  128. return ctx;
  129. }
  130. void SetGamepadImagePosition(GamepadImage *ctx, int x, int y)
  131. {
  132. if (!ctx) {
  133. return;
  134. }
  135. ctx->x = x;
  136. ctx->y = y;
  137. }
  138. void SetGamepadImageShowingFront(GamepadImage *ctx, SDL_bool showing_front)
  139. {
  140. if (!ctx) {
  141. return;
  142. }
  143. ctx->showing_front = showing_front;
  144. }
  145. void SetGamepadImageShowingTouchpad(GamepadImage *ctx, SDL_bool showing_touchpad)
  146. {
  147. if (!ctx) {
  148. return;
  149. }
  150. ctx->showing_touchpad = showing_touchpad;
  151. }
  152. void GetGamepadImageArea(GamepadImage *ctx, int *x, int *y, int *width, int *height)
  153. {
  154. if (!ctx) {
  155. if (x) {
  156. *x = 0;
  157. }
  158. if (y) {
  159. *y = 0;
  160. }
  161. if (width) {
  162. *width = 0;
  163. }
  164. if (height) {
  165. *height = 0;
  166. }
  167. return;
  168. }
  169. if (x) {
  170. *x = ctx->x;
  171. }
  172. if (y) {
  173. *y = ctx->y;
  174. }
  175. if (width) {
  176. *width = ctx->gamepad_width;
  177. }
  178. if (height) {
  179. *height = ctx->gamepad_height;
  180. if (ctx->showing_touchpad) {
  181. *height += ctx->touchpad_height;
  182. }
  183. }
  184. }
  185. int GetGamepadImageButtonWidth(GamepadImage *ctx)
  186. {
  187. if (!ctx) {
  188. return 0;
  189. }
  190. return ctx->button_width;
  191. }
  192. int GetGamepadImageButtonHeight(GamepadImage *ctx)
  193. {
  194. if (!ctx) {
  195. return 0;
  196. }
  197. return ctx->button_height;
  198. }
  199. int GetGamepadImageAxisWidth(GamepadImage *ctx)
  200. {
  201. if (!ctx) {
  202. return 0;
  203. }
  204. return ctx->axis_width;
  205. }
  206. int GetGamepadImageAxisHeight(GamepadImage *ctx)
  207. {
  208. if (!ctx) {
  209. return 0;
  210. }
  211. return ctx->axis_height;
  212. }
  213. SDL_GamepadButton GetGamepadImageButtonAt(GamepadImage *ctx, float x, float y)
  214. {
  215. SDL_FPoint point;
  216. int i;
  217. if (!ctx) {
  218. return SDL_GAMEPAD_BUTTON_INVALID;
  219. }
  220. point.x = x;
  221. point.y = y;
  222. for (i = 0; i < SDL_arraysize(button_positions); ++i) {
  223. SDL_bool on_front = SDL_TRUE;
  224. if (i >= SDL_GAMEPAD_BUTTON_PADDLE1 && i <= SDL_GAMEPAD_BUTTON_PADDLE4) {
  225. on_front = SDL_FALSE;
  226. }
  227. if (on_front == ctx->showing_front) {
  228. SDL_FRect rect;
  229. rect.x = (float)ctx->x + button_positions[i].x - ctx->button_width / 2;
  230. rect.y = (float)ctx->y + button_positions[i].y - ctx->button_height / 2;
  231. rect.w = (float)ctx->button_width;
  232. rect.h = (float)ctx->button_height;
  233. if (SDL_PointInRectFloat(&point, &rect)) {
  234. return (SDL_GamepadButton)i;
  235. }
  236. }
  237. }
  238. return SDL_GAMEPAD_BUTTON_INVALID;
  239. }
  240. SDL_GamepadAxis GetGamepadImageAxisAt(GamepadImage *ctx, float x, float y)
  241. {
  242. SDL_FPoint point;
  243. int i;
  244. if (!ctx) {
  245. return SDL_GAMEPAD_AXIS_INVALID;
  246. }
  247. point.x = x;
  248. point.y = y;
  249. if (ctx->showing_front) {
  250. for (i = 0; i < SDL_arraysize(axis_positions); ++i) {
  251. SDL_FRect rect;
  252. rect.x = (float)ctx->x + axis_positions[i].x - ctx->axis_width / 2;
  253. rect.y = (float)ctx->y + axis_positions[i].y - ctx->axis_height / 2;
  254. rect.w = (float)ctx->axis_width;
  255. rect.h = (float)ctx->axis_height;
  256. if (SDL_PointInRectFloat(&point, &rect)) {
  257. return (SDL_GamepadAxis)i;
  258. }
  259. }
  260. }
  261. return SDL_GAMEPAD_AXIS_INVALID;
  262. }
  263. void ClearGamepadImage(GamepadImage *ctx)
  264. {
  265. if (!ctx) {
  266. return;
  267. }
  268. SDL_zeroa(ctx->buttons);
  269. SDL_zeroa(ctx->axes);
  270. }
  271. void SetGamepadImageButton(GamepadImage *ctx, SDL_GamepadButton button, SDL_bool active)
  272. {
  273. if (!ctx) {
  274. return;
  275. }
  276. ctx->buttons[button] = active;
  277. }
  278. void SetGamepadImageAxis(GamepadImage *ctx, SDL_GamepadAxis axis, int direction)
  279. {
  280. if (!ctx) {
  281. return;
  282. }
  283. ctx->axes[axis] = direction;
  284. }
  285. void UpdateGamepadImageFromGamepad(GamepadImage *ctx, SDL_Gamepad *gamepad)
  286. {
  287. int i;
  288. if (!ctx) {
  289. return;
  290. }
  291. for (i = 0; i < SDL_GAMEPAD_BUTTON_TOUCHPAD; ++i) {
  292. const SDL_GamepadButton button = (SDL_GamepadButton)i;
  293. if (SDL_GetGamepadButton(gamepad, button) == SDL_PRESSED) {
  294. SetGamepadImageButton(ctx, button, SDL_TRUE);
  295. } else {
  296. SetGamepadImageButton(ctx, button, SDL_FALSE);
  297. }
  298. }
  299. for (i = 0; i < SDL_GAMEPAD_AXIS_MAX; ++i) {
  300. const SDL_GamepadAxis axis = (SDL_GamepadAxis)i;
  301. const Sint16 deadzone = 8000; /* !!! FIXME: real deadzone */
  302. const Sint16 value = SDL_GetGamepadAxis(gamepad, axis);
  303. if (value < -deadzone) {
  304. SetGamepadImageAxis(ctx, axis, -1);
  305. } else if (value > deadzone) {
  306. SetGamepadImageAxis(ctx, axis, 1);
  307. } else {
  308. SetGamepadImageAxis(ctx, axis, 0);
  309. }
  310. }
  311. if (SDL_GetNumGamepadTouchpads(gamepad) > 0) {
  312. int num_fingers = SDL_GetNumGamepadTouchpadFingers(gamepad, 0);
  313. if (num_fingers != ctx->num_fingers) {
  314. GamepadTouchpadFinger *fingers = (GamepadTouchpadFinger *)SDL_realloc(ctx->fingers, num_fingers * sizeof(*fingers));
  315. if (fingers) {
  316. ctx->fingers = fingers;
  317. ctx->num_fingers = num_fingers;
  318. } else {
  319. num_fingers = SDL_min(ctx->num_fingers, num_fingers);
  320. }
  321. }
  322. for (i = 0; i < num_fingers; ++i) {
  323. GamepadTouchpadFinger *finger = &ctx->fingers[i];
  324. SDL_GetGamepadTouchpadFinger(gamepad, 0, i, &finger->state, &finger->x, &finger->y, &finger->pressure);
  325. }
  326. }
  327. }
  328. void RenderGamepadImage(GamepadImage *ctx)
  329. {
  330. SDL_FRect dst;
  331. int i;
  332. if (!ctx) {
  333. return;
  334. }
  335. dst.x = (float)ctx->x;
  336. dst.y = (float)ctx->y;
  337. dst.w = (float)ctx->gamepad_width;
  338. dst.h = (float)ctx->gamepad_height;
  339. if (ctx->showing_front) {
  340. SDL_RenderTexture(ctx->renderer, ctx->front_texture, NULL, &dst);
  341. } else {
  342. SDL_RenderTexture(ctx->renderer, ctx->back_texture, NULL, &dst);
  343. }
  344. for (i = 0; i < SDL_arraysize(button_positions); ++i) {
  345. if (ctx->buttons[i]) {
  346. SDL_bool on_front = SDL_TRUE;
  347. if (i >= SDL_GAMEPAD_BUTTON_PADDLE1 && i <= SDL_GAMEPAD_BUTTON_PADDLE4) {
  348. on_front = SDL_FALSE;
  349. }
  350. if (on_front == ctx->showing_front) {
  351. dst.x = (float)ctx->x + button_positions[i].x - ctx->button_width / 2;
  352. dst.y = (float)ctx->y + button_positions[i].y - ctx->button_height / 2;
  353. dst.w = (float)ctx->button_width;
  354. dst.h = (float)ctx->button_height;
  355. SDL_RenderTexture(ctx->renderer, ctx->button_texture, NULL, &dst);
  356. }
  357. }
  358. }
  359. if (ctx->showing_front) {
  360. for (i = 0; i < SDL_arraysize(axis_positions); ++i) {
  361. if (ctx->axes[i] < 0) {
  362. const double angle = axis_positions[i].angle;
  363. dst.x = (float)ctx->x + axis_positions[i].x - ctx->axis_width / 2;
  364. dst.y = (float)ctx->y + axis_positions[i].y - ctx->axis_height / 2;
  365. dst.w = (float)ctx->axis_width;
  366. dst.h = (float)ctx->axis_height;
  367. SDL_RenderTextureRotated(ctx->renderer, ctx->axis_texture, NULL, &dst, angle, NULL, SDL_FLIP_NONE);
  368. } else if (ctx->axes[i] > 0) {
  369. const double angle = axis_positions[i].angle + 180.0;
  370. dst.x = (float)ctx->x + axis_positions[i].x - ctx->axis_width / 2;
  371. dst.y = (float)ctx->y + axis_positions[i].y - ctx->axis_height / 2;
  372. dst.w = (float)ctx->axis_width;
  373. dst.h = (float)ctx->axis_height;
  374. SDL_RenderTextureRotated(ctx->renderer, ctx->axis_texture, NULL, &dst, angle, NULL, SDL_FLIP_NONE);
  375. }
  376. }
  377. }
  378. if (ctx->showing_touchpad) {
  379. dst.x = (float)ctx->x + (ctx->gamepad_width - ctx->touchpad_width) / 2;
  380. dst.y = (float)ctx->y + ctx->gamepad_height;
  381. dst.w = (float)ctx->touchpad_width;
  382. dst.h = (float)ctx->touchpad_height;
  383. SDL_RenderTexture(ctx->renderer, ctx->touchpad_texture, NULL, &dst);
  384. for (i = 0; i < ctx->num_fingers; ++i) {
  385. GamepadTouchpadFinger *finger = &ctx->fingers[i];
  386. if (finger->state) {
  387. dst.x = (float)ctx->x + (ctx->gamepad_width - ctx->touchpad_width) / 2;
  388. dst.x += touchpad_area.x + finger->x * touchpad_area.w;
  389. dst.x -= ctx->button_width / 2;
  390. dst.y = (float)ctx->y + ctx->gamepad_height;
  391. dst.y += touchpad_area.y + finger->y * touchpad_area.h;
  392. dst.y -= ctx->button_height / 2;
  393. dst.w = (float)ctx->button_width;
  394. dst.h = (float)ctx->button_height;
  395. SDL_SetTextureAlphaMod(ctx->button_texture, (Uint8)(finger->pressure * SDL_ALPHA_OPAQUE));
  396. SDL_RenderTexture(ctx->renderer, ctx->button_texture, NULL, &dst);
  397. SDL_SetTextureAlphaMod(ctx->button_texture, SDL_ALPHA_OPAQUE);
  398. }
  399. }
  400. }
  401. }
  402. void DestroyGamepadImage(GamepadImage *ctx)
  403. {
  404. if (ctx) {
  405. SDL_DestroyTexture(ctx->front_texture);
  406. SDL_DestroyTexture(ctx->back_texture);
  407. SDL_DestroyTexture(ctx->button_texture);
  408. SDL_DestroyTexture(ctx->axis_texture);
  409. SDL_free(ctx);
  410. }
  411. }
  412. static const char *gamepad_button_names[] = {
  413. "A",
  414. "B",
  415. "X",
  416. "Y",
  417. "Back",
  418. "Guide",
  419. "Start",
  420. "Left Stick",
  421. "Right Stick",
  422. "Left Shoulder",
  423. "Right Shoulder",
  424. "DPAD Up",
  425. "DPAD Down",
  426. "DPAD Left",
  427. "DPAD Right",
  428. "Misc1",
  429. "Paddle1",
  430. "Paddle2",
  431. "Paddle3",
  432. "Paddle4",
  433. "Touchpad",
  434. };
  435. SDL_COMPILE_TIME_ASSERT(gamepad_button_names, SDL_arraysize(gamepad_button_names) == SDL_GAMEPAD_BUTTON_MAX);
  436. static const char *gamepad_axis_names[] = {
  437. "LeftX",
  438. "RightX",
  439. "RightX",
  440. "RightY",
  441. "Left Trigger",
  442. "Right Trigger",
  443. };
  444. SDL_COMPILE_TIME_ASSERT(gamepad_axis_names, SDL_arraysize(gamepad_axis_names) == SDL_GAMEPAD_AXIS_MAX);
  445. struct GamepadDisplay
  446. {
  447. SDL_Renderer *renderer;
  448. SDL_Texture *button_texture;
  449. SDL_Texture *arrow_texture;
  450. int button_width;
  451. int button_height;
  452. int arrow_width;
  453. int arrow_height;
  454. SDL_Rect area;
  455. };
  456. GamepadDisplay *CreateGamepadDisplay(SDL_Renderer *renderer)
  457. {
  458. GamepadDisplay *ctx = SDL_calloc(1, sizeof(*ctx));
  459. if (ctx) {
  460. ctx->renderer = renderer;
  461. ctx->button_texture = CreateTexture(renderer, gamepad_button_small_bmp, gamepad_button_small_bmp_len);
  462. SDL_QueryTexture(ctx->button_texture, NULL, NULL, &ctx->button_width, &ctx->button_height);
  463. ctx->arrow_texture = CreateTexture(renderer, gamepad_axis_arrow_bmp, gamepad_axis_arrow_bmp_len);
  464. SDL_QueryTexture(ctx->arrow_texture, NULL, NULL, &ctx->arrow_width, &ctx->arrow_height);
  465. }
  466. return ctx;
  467. }
  468. void SetGamepadDisplayArea(GamepadDisplay *ctx, int x, int y, int w, int h)
  469. {
  470. if (!ctx) {
  471. return;
  472. }
  473. ctx->area.x = x;
  474. ctx->area.y = y;
  475. ctx->area.w = w;
  476. ctx->area.h = h;
  477. }
  478. void RenderGamepadDisplay(GamepadDisplay *ctx, SDL_Gamepad *gamepad)
  479. {
  480. float x, y;
  481. int i;
  482. char text[32];
  483. const float margin = 8.0f;
  484. const float center = ctx->area.w / 2.0f;
  485. const float arrow_extent = 48.0f;
  486. SDL_FRect dst, rect;
  487. Uint8 r, g, b, a;
  488. SDL_GetRenderDrawColor(ctx->renderer, &r, &g, &b, &a);
  489. x = ctx->area.x + margin;
  490. y = ctx->area.y + margin;
  491. for (i = 0; i < SDL_GAMEPAD_BUTTON_MAX; ++i) {
  492. if (SDL_GamepadHasButton(gamepad, (SDL_GamepadButton)i)) {
  493. SDL_snprintf(text, sizeof(text), "%s:", gamepad_button_names[i]);
  494. SDLTest_DrawString(ctx->renderer, x + center - SDL_strlen(text) * FONT_CHARACTER_SIZE, y, text);
  495. if (SDL_GetGamepadButton(gamepad, (SDL_GamepadButton)i)) {
  496. SDL_SetTextureColorMod(ctx->button_texture, 10, 255, 21);
  497. } else {
  498. SDL_SetTextureColorMod(ctx->button_texture, 255, 255, 255);
  499. }
  500. dst.x = x + center + 2.0f;
  501. dst.y = y + FONT_CHARACTER_SIZE / 2 - ctx->button_height / 2;
  502. dst.w = (float)ctx->button_width;
  503. dst.h = (float)ctx->button_height;
  504. SDL_RenderTexture(ctx->renderer, ctx->button_texture, NULL, &dst);
  505. y += ctx->button_height + 2.0f;
  506. }
  507. }
  508. for (i = 0; i < SDL_GAMEPAD_AXIS_MAX; ++i) {
  509. if (SDL_GamepadHasAxis(gamepad, (SDL_GamepadAxis)i)) {
  510. SDL_bool has_negative = (i != SDL_GAMEPAD_AXIS_LEFT_TRIGGER && i != SDL_GAMEPAD_AXIS_RIGHT_TRIGGER);
  511. Sint16 value = SDL_GetGamepadAxis(gamepad, (SDL_GamepadAxis)i);
  512. SDL_snprintf(text, sizeof(text), "%s:", gamepad_axis_names[i]);
  513. SDLTest_DrawString(ctx->renderer, x + center - SDL_strlen(text) * FONT_CHARACTER_SIZE, y, text);
  514. dst.x = x + center + 2.0f;
  515. dst.y = y + FONT_CHARACTER_SIZE / 2 - ctx->arrow_height / 2;
  516. dst.w = (float)ctx->arrow_width;
  517. dst.h = (float)ctx->arrow_height;
  518. if (has_negative) {
  519. if (value == SDL_MIN_SINT16) {
  520. SDL_SetTextureColorMod(ctx->arrow_texture, 10, 255, 21);
  521. } else {
  522. SDL_SetTextureColorMod(ctx->arrow_texture, 255, 255, 255);
  523. }
  524. SDL_RenderTextureRotated(ctx->renderer, ctx->arrow_texture, NULL, &dst, 0.0f, NULL, SDL_FLIP_HORIZONTAL);
  525. }
  526. dst.x += (float)ctx->arrow_width;
  527. SDL_SetRenderDrawColor(ctx->renderer, 200, 200, 200, SDL_ALPHA_OPAQUE);
  528. rect.x = dst.x + arrow_extent - 2.0f;
  529. rect.y = dst.y;
  530. rect.w = 4.0f;
  531. rect.h = (float)ctx->arrow_height;
  532. SDL_RenderFillRect(ctx->renderer, &rect);
  533. SDL_SetRenderDrawColor(ctx->renderer, r, g, b, a);
  534. if (value < 0) {
  535. SDL_SetRenderDrawColor(ctx->renderer, 8, 200, 16, SDL_ALPHA_OPAQUE);
  536. rect.w = ((float)value / SDL_MIN_SINT16) * arrow_extent;
  537. rect.x = dst.x + arrow_extent - rect.w;
  538. rect.y = dst.y + ctx->arrow_height * 0.25f;
  539. rect.h = ctx->arrow_height / 2.0f;
  540. SDL_RenderFillRect(ctx->renderer, &rect);
  541. }
  542. dst.x += arrow_extent;
  543. if (value > 0) {
  544. SDL_SetRenderDrawColor(ctx->renderer, 8, 200, 16, SDL_ALPHA_OPAQUE);
  545. rect.w = ((float)value / SDL_MAX_SINT16) * arrow_extent;
  546. rect.x = dst.x;
  547. rect.y = dst.y + ctx->arrow_height * 0.25f;
  548. rect.h = ctx->arrow_height / 2.0f;
  549. SDL_RenderFillRect(ctx->renderer, &rect);
  550. }
  551. dst.x += arrow_extent;
  552. if (value == SDL_MAX_SINT16) {
  553. SDL_SetTextureColorMod(ctx->arrow_texture, 10, 255, 21);
  554. } else {
  555. SDL_SetTextureColorMod(ctx->arrow_texture, 255, 255, 255);
  556. }
  557. SDL_RenderTexture(ctx->renderer, ctx->arrow_texture, NULL, &dst);
  558. SDL_SetRenderDrawColor(ctx->renderer, r, g, b, a);
  559. y += ctx->button_height + 2;
  560. }
  561. }
  562. }
  563. void DestroyGamepadDisplay(GamepadDisplay *ctx)
  564. {
  565. SDL_free(ctx);
  566. }
  567. struct JoystickDisplay
  568. {
  569. SDL_Renderer *renderer;
  570. SDL_Texture *button_texture;
  571. SDL_Texture *arrow_texture;
  572. int button_width;
  573. int button_height;
  574. int arrow_width;
  575. int arrow_height;
  576. SDL_Rect area;
  577. };
  578. JoystickDisplay *CreateJoystickDisplay(SDL_Renderer *renderer)
  579. {
  580. JoystickDisplay *ctx = SDL_calloc(1, sizeof(*ctx));
  581. if (ctx) {
  582. ctx->renderer = renderer;
  583. ctx->button_texture = CreateTexture(renderer, gamepad_button_small_bmp, gamepad_button_small_bmp_len);
  584. SDL_QueryTexture(ctx->button_texture, NULL, NULL, &ctx->button_width, &ctx->button_height);
  585. ctx->arrow_texture = CreateTexture(renderer, gamepad_axis_arrow_bmp, gamepad_axis_arrow_bmp_len);
  586. SDL_QueryTexture(ctx->arrow_texture, NULL, NULL, &ctx->arrow_width, &ctx->arrow_height);
  587. }
  588. return ctx;
  589. }
  590. void SetJoystickDisplayArea(JoystickDisplay *ctx, int x, int y, int w, int h)
  591. {
  592. if (!ctx) {
  593. return;
  594. }
  595. ctx->area.x = x;
  596. ctx->area.y = y;
  597. ctx->area.w = w;
  598. ctx->area.h = h;
  599. }
  600. void RenderJoystickDisplay(JoystickDisplay *ctx, SDL_Joystick *joystick)
  601. {
  602. float x, y;
  603. int i;
  604. int nbuttons = SDL_GetNumJoystickButtons(joystick);
  605. int naxes = SDL_GetNumJoystickAxes(joystick);
  606. int nhats = SDL_GetNumJoystickHats(joystick);
  607. char text[32];
  608. const float margin = 8.0f;
  609. const float center = 80.0f;
  610. const float arrow_extent = 48.0f;
  611. SDL_FRect dst, rect;
  612. Uint8 r, g, b, a;
  613. SDL_GetRenderDrawColor(ctx->renderer, &r, &g, &b, &a);
  614. x = (float)ctx->area.x + margin;
  615. y = (float)ctx->area.y + margin;
  616. if (nbuttons > 0) {
  617. SDLTest_DrawString(ctx->renderer, x, y, "BUTTONS");
  618. y += FONT_LINE_HEIGHT + 2;
  619. for (i = 0; i < nbuttons; ++i) {
  620. SDL_snprintf(text, sizeof(text), "%2.d:", i);
  621. SDLTest_DrawString(ctx->renderer, x, y, text);
  622. if (SDL_GetJoystickButton(joystick, (Uint8)i)) {
  623. SDL_SetTextureColorMod(ctx->button_texture, 10, 255, 21);
  624. } else {
  625. SDL_SetTextureColorMod(ctx->button_texture, 255, 255, 255);
  626. }
  627. dst.x = x + FONT_CHARACTER_SIZE * SDL_strlen(text) + 2;
  628. dst.y = y + FONT_CHARACTER_SIZE / 2 - ctx->button_height / 2;
  629. dst.w = (float)ctx->button_width;
  630. dst.h = (float)ctx->button_height;
  631. SDL_RenderTexture(ctx->renderer, ctx->button_texture, NULL, &dst);
  632. y += ctx->button_height + 2;
  633. }
  634. }
  635. x = (float)ctx->area.x + margin + center + margin;
  636. y = (float)ctx->area.y + margin;
  637. if (naxes > 0) {
  638. SDLTest_DrawString(ctx->renderer, x, y, "AXES");
  639. y += FONT_LINE_HEIGHT + 2;
  640. for (i = 0; i < naxes; ++i) {
  641. Sint16 value = SDL_GetJoystickAxis(joystick, i);
  642. SDL_snprintf(text, sizeof(text), "%d:", i);
  643. SDLTest_DrawString(ctx->renderer, x, y, text);
  644. dst.x = x + FONT_CHARACTER_SIZE * SDL_strlen(text) + 2.0f;
  645. dst.y = y + FONT_CHARACTER_SIZE / 2 - ctx->arrow_height / 2;
  646. dst.w = (float)ctx->arrow_width;
  647. dst.h = (float)ctx->arrow_height;
  648. if (value == SDL_MIN_SINT16) {
  649. SDL_SetTextureColorMod(ctx->arrow_texture, 10, 255, 21);
  650. } else {
  651. SDL_SetTextureColorMod(ctx->arrow_texture, 255, 255, 255);
  652. }
  653. SDL_RenderTextureRotated(ctx->renderer, ctx->arrow_texture, NULL, &dst, 0.0f, NULL, SDL_FLIP_HORIZONTAL);
  654. dst.x += (float)ctx->arrow_width;
  655. SDL_SetRenderDrawColor(ctx->renderer, 200, 200, 200, SDL_ALPHA_OPAQUE);
  656. rect.x = dst.x + arrow_extent - 2.0f;
  657. rect.y = dst.y;
  658. rect.w = 4.0f;
  659. rect.h = (float)ctx->arrow_height;
  660. SDL_RenderFillRect(ctx->renderer, &rect);
  661. SDL_SetRenderDrawColor(ctx->renderer, r, g, b, a);
  662. if (value < 0) {
  663. SDL_SetRenderDrawColor(ctx->renderer, 8, 200, 16, SDL_ALPHA_OPAQUE);
  664. rect.w = ((float)value / SDL_MIN_SINT16) * arrow_extent;
  665. rect.x = dst.x + arrow_extent - rect.w;
  666. rect.y = dst.y + ctx->arrow_height * 0.25f;
  667. rect.h = ctx->arrow_height / 2.0f;
  668. SDL_RenderFillRect(ctx->renderer, &rect);
  669. }
  670. dst.x += arrow_extent;
  671. if (value > 0) {
  672. SDL_SetRenderDrawColor(ctx->renderer, 8, 200, 16, SDL_ALPHA_OPAQUE);
  673. rect.w = ((float)value / SDL_MAX_SINT16) * arrow_extent;
  674. rect.x = dst.x;
  675. rect.y = dst.y + ctx->arrow_height * 0.25f;
  676. rect.h = ctx->arrow_height / 2.0f;
  677. SDL_RenderFillRect(ctx->renderer, &rect);
  678. }
  679. dst.x += arrow_extent;
  680. if (value == SDL_MAX_SINT16) {
  681. SDL_SetTextureColorMod(ctx->arrow_texture, 10, 255, 21);
  682. } else {
  683. SDL_SetTextureColorMod(ctx->arrow_texture, 255, 255, 255);
  684. }
  685. SDL_RenderTexture(ctx->renderer, ctx->arrow_texture, NULL, &dst);
  686. SDL_SetRenderDrawColor(ctx->renderer, r, g, b, a);
  687. y += ctx->button_height + 2;
  688. }
  689. }
  690. y += FONT_LINE_HEIGHT + 2;
  691. if (nhats > 0) {
  692. SDLTest_DrawString(ctx->renderer, x, y, "HATS");
  693. y += FONT_LINE_HEIGHT + 2 + 1.5f * ctx->button_height - FONT_CHARACTER_SIZE / 2;
  694. for (i = 0; i < nhats; ++i) {
  695. Uint8 value = SDL_GetJoystickHat(joystick, i);
  696. SDL_snprintf(text, sizeof(text), "%d:", i);
  697. SDLTest_DrawString(ctx->renderer, x, y, text);
  698. if (value & SDL_HAT_LEFT) {
  699. SDL_SetTextureColorMod(ctx->button_texture, 10, 255, 21);
  700. } else {
  701. SDL_SetTextureColorMod(ctx->button_texture, 255, 255, 255);
  702. }
  703. dst.x = x + FONT_CHARACTER_SIZE * SDL_strlen(text) + 2;
  704. dst.y = y + FONT_CHARACTER_SIZE / 2 - ctx->button_height / 2;
  705. dst.w = (float)ctx->button_width;
  706. dst.h = (float)ctx->button_height;
  707. SDL_RenderTexture(ctx->renderer, ctx->button_texture, NULL, &dst);
  708. if (value & SDL_HAT_UP) {
  709. SDL_SetTextureColorMod(ctx->button_texture, 10, 255, 21);
  710. } else {
  711. SDL_SetTextureColorMod(ctx->button_texture, 255, 255, 255);
  712. }
  713. dst.x += (float)ctx->button_width;
  714. dst.y -= (float)ctx->button_height;
  715. SDL_RenderTexture(ctx->renderer, ctx->button_texture, NULL, &dst);
  716. if (value & SDL_HAT_DOWN) {
  717. SDL_SetTextureColorMod(ctx->button_texture, 10, 255, 21);
  718. } else {
  719. SDL_SetTextureColorMod(ctx->button_texture, 255, 255, 255);
  720. }
  721. dst.y += (float)ctx->button_height * 2;
  722. SDL_RenderTexture(ctx->renderer, ctx->button_texture, NULL, &dst);
  723. if (value & SDL_HAT_RIGHT) {
  724. SDL_SetTextureColorMod(ctx->button_texture, 10, 255, 21);
  725. } else {
  726. SDL_SetTextureColorMod(ctx->button_texture, 255, 255, 255);
  727. }
  728. dst.x += (float)ctx->button_width;
  729. dst.y = y + FONT_CHARACTER_SIZE / 2 - ctx->button_height / 2;
  730. SDL_RenderTexture(ctx->renderer, ctx->button_texture, NULL, &dst);
  731. y += 3 * ctx->button_height + 2;
  732. }
  733. }
  734. }
  735. void DestroyJoystickDisplay(JoystickDisplay *ctx)
  736. {
  737. SDL_free(ctx);
  738. }