1 /*
2 Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org>
3
4 This software is provided 'as-is', without any express or implied
5 warranty. In no event will the authors be held liable for any damages
6 arising from the use of this software.
7
8 Permission is granted to anyone to use this software for any purpose,
9 including commercial applications, and to alter it and redistribute it
10 freely.
11 */
12
13 /* Game controller mapping generator */
14 /* Gabriel Jacobo <gabomdq@gmail.com> */
15
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <string.h>
19
20 #include "SDL.h"
21
22 #ifndef SDL_JOYSTICK_DISABLED
23
24 /* Define this for verbose output while mapping controllers */
25 #define DEBUG_CONTROLLERMAP
26
27 #ifdef __IPHONEOS__
28 #define SCREEN_WIDTH 320
29 #define SCREEN_HEIGHT 480
30 #else
31 #define SCREEN_WIDTH 512
32 #define SCREEN_HEIGHT 320
33 #endif
34
35 #define MARKER_BUTTON 1
36 #define MARKER_AXIS 2
37
38 enum
39 {
40 SDL_CONTROLLER_BINDING_AXIS_LEFTX_NEGATIVE,
41 SDL_CONTROLLER_BINDING_AXIS_LEFTX_POSITIVE,
42 SDL_CONTROLLER_BINDING_AXIS_LEFTY_NEGATIVE,
43 SDL_CONTROLLER_BINDING_AXIS_LEFTY_POSITIVE,
44 SDL_CONTROLLER_BINDING_AXIS_RIGHTX_NEGATIVE,
45 SDL_CONTROLLER_BINDING_AXIS_RIGHTX_POSITIVE,
46 SDL_CONTROLLER_BINDING_AXIS_RIGHTY_NEGATIVE,
47 SDL_CONTROLLER_BINDING_AXIS_RIGHTY_POSITIVE,
48 SDL_CONTROLLER_BINDING_AXIS_TRIGGERLEFT,
49 SDL_CONTROLLER_BINDING_AXIS_TRIGGERRIGHT,
50 SDL_CONTROLLER_BINDING_AXIS_MAX,
51 };
52
53 #define BINDING_COUNT (SDL_CONTROLLER_BUTTON_MAX + SDL_CONTROLLER_BINDING_AXIS_MAX)
54
55 static struct
56 {
57 int x, y;
58 double angle;
59 int marker;
60
61 } s_arrBindingDisplay[BINDING_COUNT] = {
62 { 387, 167, 0.0, MARKER_BUTTON }, /* SDL_CONTROLLER_BUTTON_A */
63 { 431, 132, 0.0, MARKER_BUTTON }, /* SDL_CONTROLLER_BUTTON_B */
64 { 342, 132, 0.0, MARKER_BUTTON }, /* SDL_CONTROLLER_BUTTON_X */
65 { 389, 101, 0.0, MARKER_BUTTON }, /* SDL_CONTROLLER_BUTTON_Y */
66 { 174, 132, 0.0, MARKER_BUTTON }, /* SDL_CONTROLLER_BUTTON_BACK */
67 { 233, 132, 0.0, MARKER_BUTTON }, /* SDL_CONTROLLER_BUTTON_GUIDE */
68 { 289, 132, 0.0, MARKER_BUTTON }, /* SDL_CONTROLLER_BUTTON_START */
69 { 75, 154, 0.0, MARKER_BUTTON }, /* SDL_CONTROLLER_BUTTON_LEFTSTICK */
70 { 305, 230, 0.0, MARKER_BUTTON }, /* SDL_CONTROLLER_BUTTON_RIGHTSTICK */
71 { 77, 40, 0.0, MARKER_BUTTON }, /* SDL_CONTROLLER_BUTTON_LEFTSHOULDER */
72 { 396, 36, 0.0, MARKER_BUTTON }, /* SDL_CONTROLLER_BUTTON_RIGHTSHOULDER */
73 { 154, 188, 0.0, MARKER_BUTTON }, /* SDL_CONTROLLER_BUTTON_DPAD_UP */
74 { 154, 249, 0.0, MARKER_BUTTON }, /* SDL_CONTROLLER_BUTTON_DPAD_DOWN */
75 { 116, 217, 0.0, MARKER_BUTTON }, /* SDL_CONTROLLER_BUTTON_DPAD_LEFT */
76 { 186, 217, 0.0, MARKER_BUTTON }, /* SDL_CONTROLLER_BUTTON_DPAD_RIGHT */
77 { 74, 153, 270.0, MARKER_AXIS }, /* SDL_CONTROLLER_BINDING_AXIS_LEFTX_NEGATIVE */
78 { 74, 153, 90.0, MARKER_AXIS }, /* SDL_CONTROLLER_BINDING_AXIS_LEFTX_POSITIVE */
79 { 74, 153, 0.0, MARKER_AXIS }, /* SDL_CONTROLLER_BINDING_AXIS_LEFTY_NEGATIVE */
80 { 74, 153, 180.0, MARKER_AXIS }, /* SDL_CONTROLLER_BINDING_AXIS_LEFTY_POSITIVE */
81 { 306, 231, 270.0, MARKER_AXIS }, /* SDL_CONTROLLER_BINDING_AXIS_RIGHTX_NEGATIVE */
82 { 306, 231, 90.0, MARKER_AXIS }, /* SDL_CONTROLLER_BINDING_AXIS_RIGHTX_POSITIVE */
83 { 306, 231, 0.0, MARKER_AXIS }, /* SDL_CONTROLLER_BINDING_AXIS_RIGHTY_NEGATIVE */
84 { 306, 231, 180.0, MARKER_AXIS }, /* SDL_CONTROLLER_BINDING_AXIS_RIGHTY_POSITIVE */
85 { 91, -20, 180.0, MARKER_AXIS }, /* SDL_CONTROLLER_BINDING_AXIS_TRIGGERLEFT */
86 { 375, -20, 180.0, MARKER_AXIS }, /* SDL_CONTROLLER_BINDING_AXIS_TRIGGERRIGHT */
87 };
88
89 static int s_arrBindingOrder[BINDING_COUNT] = {
90 SDL_CONTROLLER_BUTTON_A,
91 SDL_CONTROLLER_BUTTON_B,
92 SDL_CONTROLLER_BUTTON_Y,
93 SDL_CONTROLLER_BUTTON_X,
94 SDL_CONTROLLER_BUTTON_MAX + SDL_CONTROLLER_BINDING_AXIS_LEFTX_NEGATIVE,
95 SDL_CONTROLLER_BUTTON_MAX + SDL_CONTROLLER_BINDING_AXIS_LEFTX_POSITIVE,
96 SDL_CONTROLLER_BUTTON_MAX + SDL_CONTROLLER_BINDING_AXIS_LEFTY_NEGATIVE,
97 SDL_CONTROLLER_BUTTON_MAX + SDL_CONTROLLER_BINDING_AXIS_LEFTY_POSITIVE,
98 SDL_CONTROLLER_BUTTON_LEFTSTICK,
99 SDL_CONTROLLER_BUTTON_MAX + SDL_CONTROLLER_BINDING_AXIS_RIGHTX_NEGATIVE,
100 SDL_CONTROLLER_BUTTON_MAX + SDL_CONTROLLER_BINDING_AXIS_RIGHTX_POSITIVE,
101 SDL_CONTROLLER_BUTTON_MAX + SDL_CONTROLLER_BINDING_AXIS_RIGHTY_NEGATIVE,
102 SDL_CONTROLLER_BUTTON_MAX + SDL_CONTROLLER_BINDING_AXIS_RIGHTY_POSITIVE,
103 SDL_CONTROLLER_BUTTON_RIGHTSTICK,
104 SDL_CONTROLLER_BUTTON_LEFTSHOULDER,
105 SDL_CONTROLLER_BUTTON_MAX + SDL_CONTROLLER_BINDING_AXIS_TRIGGERLEFT,
106 SDL_CONTROLLER_BUTTON_RIGHTSHOULDER,
107 SDL_CONTROLLER_BUTTON_MAX + SDL_CONTROLLER_BINDING_AXIS_TRIGGERRIGHT,
108 SDL_CONTROLLER_BUTTON_DPAD_UP,
109 SDL_CONTROLLER_BUTTON_DPAD_RIGHT,
110 SDL_CONTROLLER_BUTTON_DPAD_DOWN,
111 SDL_CONTROLLER_BUTTON_DPAD_LEFT,
112 SDL_CONTROLLER_BUTTON_BACK,
113 SDL_CONTROLLER_BUTTON_GUIDE,
114 SDL_CONTROLLER_BUTTON_START,
115 };
116
117 typedef struct
118 {
119 SDL_GameControllerBindType bindType;
120 union
121 {
122 int button;
123
124 struct {
125 int axis;
126 int axis_min;
127 int axis_max;
128 } axis;
129
130 struct {
131 int hat;
132 int hat_mask;
133 } hat;
134
135 } value;
136
137 SDL_bool committed;
138
139 } SDL_GameControllerExtendedBind;
140
141 static SDL_GameControllerExtendedBind s_arrBindings[BINDING_COUNT];
142
143 typedef struct
144 {
145 SDL_bool m_bMoving;
146 int m_nLastValue;
147 int m_nStartingValue;
148 int m_nFarthestValue;
149 } AxisState;
150
151 static int s_nNumAxes;
152 static AxisState *s_arrAxisState;
153
154 static int s_iCurrentBinding;
155 static Uint32 s_unPendingAdvanceTime;
156 static SDL_bool s_bBindingComplete;
157
158 static SDL_Window *window;
159 static SDL_bool done = SDL_FALSE;
160
161 SDL_Texture *
LoadTexture(SDL_Renderer * renderer,const char * file,SDL_bool transparent)162 LoadTexture(SDL_Renderer *renderer, const char *file, SDL_bool transparent)
163 {
164 SDL_Surface *temp;
165 SDL_Texture *texture;
166
167 /* Load the sprite image */
168 temp = SDL_LoadBMP(file);
169 if (temp == NULL) {
170 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't load %s: %s", file, SDL_GetError());
171 return NULL;
172 }
173
174 /* Set transparent pixel as the pixel at (0,0) */
175 if (transparent) {
176 if (temp->format->palette) {
177 SDL_SetColorKey(temp, SDL_TRUE, *(Uint8 *) temp->pixels);
178 }
179 }
180
181 /* Create textures from the image */
182 texture = SDL_CreateTextureFromSurface(renderer, temp);
183 if (!texture) {
184 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create texture: %s\n", SDL_GetError());
185 SDL_FreeSurface(temp);
186 return NULL;
187 }
188 SDL_FreeSurface(temp);
189
190 /* We're ready to roll. :) */
191 return texture;
192 }
193
194 static int
StandardizeAxisValue(int nValue)195 StandardizeAxisValue(int nValue)
196 {
197 if (nValue > SDL_JOYSTICK_AXIS_MAX/2) {
198 return SDL_JOYSTICK_AXIS_MAX;
199 } else if (nValue < SDL_JOYSTICK_AXIS_MIN/2) {
200 return SDL_JOYSTICK_AXIS_MIN;
201 } else {
202 return 0;
203 }
204 }
205
206 static void
SetCurrentBinding(int iBinding)207 SetCurrentBinding(int iBinding)
208 {
209 int iIndex;
210 SDL_GameControllerExtendedBind *pBinding;
211
212 if (iBinding < 0) {
213 return;
214 }
215
216 if (iBinding == BINDING_COUNT) {
217 s_bBindingComplete = SDL_TRUE;
218 return;
219 }
220
221 s_iCurrentBinding = iBinding;
222
223 pBinding = &s_arrBindings[s_arrBindingOrder[s_iCurrentBinding]];
224 SDL_zerop(pBinding);
225
226 for (iIndex = 0; iIndex < s_nNumAxes; ++iIndex) {
227 s_arrAxisState[iIndex].m_nFarthestValue = s_arrAxisState[iIndex].m_nStartingValue;
228 }
229
230 s_unPendingAdvanceTime = 0;
231 }
232
233 static SDL_bool
BBindingContainsBinding(const SDL_GameControllerExtendedBind * pBindingA,const SDL_GameControllerExtendedBind * pBindingB)234 BBindingContainsBinding(const SDL_GameControllerExtendedBind *pBindingA, const SDL_GameControllerExtendedBind *pBindingB)
235 {
236 if (pBindingA->bindType != pBindingB->bindType)
237 {
238 return SDL_FALSE;
239 }
240 switch (pBindingA->bindType)
241 {
242 case SDL_CONTROLLER_BINDTYPE_AXIS:
243 if (pBindingA->value.axis.axis != pBindingB->value.axis.axis) {
244 return SDL_FALSE;
245 }
246 if (!pBindingA->committed) {
247 return SDL_FALSE;
248 }
249 {
250 int minA = SDL_min(pBindingA->value.axis.axis_min, pBindingA->value.axis.axis_max);
251 int maxA = SDL_max(pBindingA->value.axis.axis_min, pBindingA->value.axis.axis_max);
252 int minB = SDL_min(pBindingB->value.axis.axis_min, pBindingB->value.axis.axis_max);
253 int maxB = SDL_max(pBindingB->value.axis.axis_min, pBindingB->value.axis.axis_max);
254 return (minA <= minB && maxA >= maxB);
255 }
256 /* Not reached */
257 default:
258 return SDL_memcmp(pBindingA, pBindingB, sizeof(*pBindingA)) == 0;
259 }
260 }
261
262 static void
ConfigureBinding(const SDL_GameControllerExtendedBind * pBinding)263 ConfigureBinding(const SDL_GameControllerExtendedBind *pBinding)
264 {
265 SDL_GameControllerExtendedBind *pCurrent;
266 int iIndex;
267 int iCurrentElement = s_arrBindingOrder[s_iCurrentBinding];
268
269 /* Do we already have this binding? */
270 for (iIndex = 0; iIndex < SDL_arraysize(s_arrBindings); ++iIndex) {
271 pCurrent = &s_arrBindings[iIndex];
272 if (BBindingContainsBinding(pCurrent, pBinding)) {
273 if (iIndex == SDL_CONTROLLER_BUTTON_A && iCurrentElement != SDL_CONTROLLER_BUTTON_B) {
274 /* Skip to the next binding */
275 SetCurrentBinding(s_iCurrentBinding + 1);
276 return;
277 }
278
279 if (iIndex == SDL_CONTROLLER_BUTTON_B) {
280 /* Go back to the previous binding */
281 SetCurrentBinding(s_iCurrentBinding - 1);
282 return;
283 }
284
285 /* Already have this binding, ignore it */
286 return;
287 }
288 }
289
290 #ifdef DEBUG_CONTROLLERMAP
291 switch ( pBinding->bindType )
292 {
293 case SDL_CONTROLLER_BINDTYPE_NONE:
294 break;
295 case SDL_CONTROLLER_BINDTYPE_BUTTON:
296 SDL_Log("Configuring button binding for button %d\n", pBinding->value.button);
297 break;
298 case SDL_CONTROLLER_BINDTYPE_AXIS:
299 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");
300 break;
301 case SDL_CONTROLLER_BINDTYPE_HAT:
302 SDL_Log("Configuring hat binding for hat %d %d\n", pBinding->value.hat.hat, pBinding->value.hat.hat_mask);
303 break;
304 }
305 #endif /* DEBUG_CONTROLLERMAP */
306
307 /* Should the new binding override the existing one? */
308 pCurrent = &s_arrBindings[iCurrentElement];
309 if (pCurrent->bindType != SDL_CONTROLLER_BINDTYPE_NONE) {
310 SDL_bool bNativeDPad, bCurrentDPad;
311 SDL_bool bNativeAxis, bCurrentAxis;
312
313 bNativeDPad = (iCurrentElement == SDL_CONTROLLER_BUTTON_DPAD_UP ||
314 iCurrentElement == SDL_CONTROLLER_BUTTON_DPAD_DOWN ||
315 iCurrentElement == SDL_CONTROLLER_BUTTON_DPAD_LEFT ||
316 iCurrentElement == SDL_CONTROLLER_BUTTON_DPAD_RIGHT);
317 bCurrentDPad = (pCurrent->bindType == SDL_CONTROLLER_BINDTYPE_HAT);
318 if (bNativeDPad && bCurrentDPad) {
319 /* We already have a binding of the type we want, ignore the new one */
320 return;
321 }
322
323 bNativeAxis = (iCurrentElement >= SDL_CONTROLLER_BUTTON_MAX);
324 bCurrentAxis = (pCurrent->bindType == SDL_CONTROLLER_BINDTYPE_AXIS);
325 if (bNativeAxis == bCurrentAxis &&
326 (pBinding->bindType != SDL_CONTROLLER_BINDTYPE_AXIS ||
327 pBinding->value.axis.axis != pCurrent->value.axis.axis)) {
328 /* We already have a binding of the type we want, ignore the new one */
329 return;
330 }
331 }
332
333 *pCurrent = *pBinding;
334
335 if (pBinding->committed) {
336 s_unPendingAdvanceTime = SDL_GetTicks();
337 } else {
338 s_unPendingAdvanceTime = 0;
339 }
340 }
341
342 static SDL_bool
BMergeAxisBindings(int iIndex)343 BMergeAxisBindings(int iIndex)
344 {
345 SDL_GameControllerExtendedBind *pBindingA = &s_arrBindings[iIndex];
346 SDL_GameControllerExtendedBind *pBindingB = &s_arrBindings[iIndex+1];
347 if (pBindingA->bindType == SDL_CONTROLLER_BINDTYPE_AXIS &&
348 pBindingB->bindType == SDL_CONTROLLER_BINDTYPE_AXIS &&
349 pBindingA->value.axis.axis == pBindingB->value.axis.axis) {
350 if (pBindingA->value.axis.axis_min == pBindingB->value.axis.axis_min) {
351 pBindingA->value.axis.axis_min = pBindingA->value.axis.axis_max;
352 pBindingA->value.axis.axis_max = pBindingB->value.axis.axis_max;
353 pBindingB->bindType = SDL_CONTROLLER_BINDTYPE_NONE;
354 return SDL_TRUE;
355 }
356 }
357 return SDL_FALSE;
358 }
359
360 static void
WatchJoystick(SDL_Joystick * joystick)361 WatchJoystick(SDL_Joystick * joystick)
362 {
363 SDL_Renderer *screen = NULL;
364 SDL_Texture *background, *button, *axis, *marker;
365 const char *name = NULL;
366 SDL_Event event;
367 SDL_Rect dst;
368 Uint8 alpha=200, alpha_step = -1;
369 Uint32 alpha_ticks = 0;
370 SDL_JoystickID nJoystickID;
371
372 screen = SDL_CreateRenderer(window, -1, 0);
373 if (screen == NULL) {
374 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create renderer: %s\n", SDL_GetError());
375 return;
376 }
377
378 background = LoadTexture(screen, "controllermap.bmp", SDL_FALSE);
379 button = LoadTexture(screen, "button.bmp", SDL_TRUE);
380 axis = LoadTexture(screen, "axis.bmp", SDL_TRUE);
381 SDL_RaiseWindow(window);
382
383 /* scale for platforms that don't give you the window size you asked for. */
384 SDL_RenderSetLogicalSize(screen, SCREEN_WIDTH, SCREEN_HEIGHT);
385
386 /* Print info about the joystick we are watching */
387 name = SDL_JoystickName(joystick);
388 SDL_Log("Watching joystick %d: (%s)\n", SDL_JoystickInstanceID(joystick),
389 name ? name : "Unknown Joystick");
390 SDL_Log("Joystick has %d axes, %d hats, %d balls, and %d buttons\n",
391 SDL_JoystickNumAxes(joystick), SDL_JoystickNumHats(joystick),
392 SDL_JoystickNumBalls(joystick), SDL_JoystickNumButtons(joystick));
393
394 SDL_Log("\n\n\
395 ====================================================================================\n\
396 Press the buttons on your controller when indicated\n\
397 (Your controller may look different than the picture)\n\
398 If you want to correct a mistake, press backspace or the back button on your device\n\
399 To skip a button, press SPACE or click/touch the screen\n\
400 To exit, press ESC\n\
401 ====================================================================================\n");
402
403 nJoystickID = SDL_JoystickInstanceID(joystick);
404
405 s_nNumAxes = SDL_JoystickNumAxes(joystick);
406 s_arrAxisState = (AxisState *)SDL_calloc(s_nNumAxes, sizeof(*s_arrAxisState));
407
408 /* Skip any spurious events at start */
409 while (SDL_PollEvent(&event) > 0) {
410 continue;
411 }
412
413 /* Loop, getting joystick events! */
414 while (!done && !s_bBindingComplete) {
415 int iElement = s_arrBindingOrder[s_iCurrentBinding];
416
417 switch (s_arrBindingDisplay[iElement].marker) {
418 case MARKER_AXIS:
419 marker = axis;
420 break;
421 case MARKER_BUTTON:
422 marker = button;
423 break;
424 default:
425 break;
426 }
427
428 dst.x = s_arrBindingDisplay[iElement].x;
429 dst.y = s_arrBindingDisplay[iElement].y;
430 SDL_QueryTexture(marker, NULL, NULL, &dst.w, &dst.h);
431
432 if (SDL_GetTicks() - alpha_ticks > 5) {
433 alpha_ticks = SDL_GetTicks();
434 alpha += alpha_step;
435 if (alpha == 255) {
436 alpha_step = -1;
437 }
438 if (alpha < 128) {
439 alpha_step = 1;
440 }
441 }
442
443 SDL_SetRenderDrawColor(screen, 0xFF, 0xFF, 0xFF, SDL_ALPHA_OPAQUE);
444 SDL_RenderClear(screen);
445 SDL_RenderCopy(screen, background, NULL, NULL);
446 SDL_SetTextureAlphaMod(marker, alpha);
447 SDL_SetTextureColorMod(marker, 10, 255, 21);
448 SDL_RenderCopyEx(screen, marker, NULL, &dst, s_arrBindingDisplay[iElement].angle, NULL, SDL_FLIP_NONE);
449 SDL_RenderPresent(screen);
450
451 while (SDL_PollEvent(&event) > 0) {
452 switch (event.type) {
453 case SDL_JOYDEVICEREMOVED:
454 if (event.jaxis.which == nJoystickID) {
455 done = SDL_TRUE;
456 }
457 break;
458 case SDL_JOYAXISMOTION:
459 if (event.jaxis.which == nJoystickID) {
460 const int MAX_ALLOWED_JITTER = SDL_JOYSTICK_AXIS_MAX / 80; /* ShanWan PS3 controller needed 96 */
461 AxisState *pAxisState = &s_arrAxisState[event.jaxis.axis];
462 int nValue = event.jaxis.value;
463 int nCurrentDistance, nFarthestDistance;
464 if (!pAxisState->m_bMoving) {
465 Sint16 nInitialValue;
466 pAxisState->m_bMoving = SDL_JoystickGetAxisInitialState(joystick, event.jaxis.axis, &nInitialValue);
467 pAxisState->m_nLastValue = nValue;
468 pAxisState->m_nStartingValue = nInitialValue;
469 pAxisState->m_nFarthestValue = nInitialValue;
470 } else if (SDL_abs(nValue - pAxisState->m_nLastValue) <= MAX_ALLOWED_JITTER) {
471 break;
472 } else {
473 pAxisState->m_nLastValue = nValue;
474 }
475 nCurrentDistance = SDL_abs(nValue - pAxisState->m_nStartingValue);
476 nFarthestDistance = SDL_abs(pAxisState->m_nFarthestValue - pAxisState->m_nStartingValue);
477 if (nCurrentDistance > nFarthestDistance) {
478 pAxisState->m_nFarthestValue = nValue;
479 nFarthestDistance = SDL_abs(pAxisState->m_nFarthestValue - pAxisState->m_nStartingValue);
480 }
481
482 #ifdef DEBUG_CONTROLLERMAP
483 SDL_Log("AXIS %d nValue %d nCurrentDistance %d nFarthestDistance %d\n", event.jaxis.axis, nValue, nCurrentDistance, nFarthestDistance);
484 #endif
485 if (nFarthestDistance >= 16000) {
486 /* If we've gone out far enough and started to come back, let's bind this axis */
487 SDL_bool bCommitBinding = (nCurrentDistance <= 10000) ? SDL_TRUE : SDL_FALSE;
488 SDL_GameControllerExtendedBind binding;
489 SDL_zero(binding);
490 binding.bindType = SDL_CONTROLLER_BINDTYPE_AXIS;
491 binding.value.axis.axis = event.jaxis.axis;
492 binding.value.axis.axis_min = StandardizeAxisValue(pAxisState->m_nStartingValue);
493 binding.value.axis.axis_max = StandardizeAxisValue(pAxisState->m_nFarthestValue);
494 binding.committed = bCommitBinding;
495 ConfigureBinding(&binding);
496 }
497 }
498 break;
499 case SDL_JOYHATMOTION:
500 if (event.jhat.which == nJoystickID) {
501 if (event.jhat.value != SDL_HAT_CENTERED) {
502 SDL_GameControllerExtendedBind binding;
503
504 #ifdef DEBUG_CONTROLLERMAP
505 SDL_Log("HAT %d %d\n", event.jhat.hat, event.jhat.value);
506 #endif
507 SDL_zero(binding);
508 binding.bindType = SDL_CONTROLLER_BINDTYPE_HAT;
509 binding.value.hat.hat = event.jhat.hat;
510 binding.value.hat.hat_mask = event.jhat.value;
511 binding.committed = SDL_TRUE;
512 ConfigureBinding(&binding);
513 }
514 }
515 break;
516 case SDL_JOYBALLMOTION:
517 break;
518 case SDL_JOYBUTTONDOWN:
519 if (event.jbutton.which == nJoystickID) {
520 SDL_GameControllerExtendedBind binding;
521
522 #ifdef DEBUG_CONTROLLERMAP
523 SDL_Log("BUTTON %d\n", event.jbutton.button);
524 #endif
525 SDL_zero(binding);
526 binding.bindType = SDL_CONTROLLER_BINDTYPE_BUTTON;
527 binding.value.button = event.jbutton.button;
528 binding.committed = SDL_TRUE;
529 ConfigureBinding(&binding);
530 }
531 break;
532 case SDL_FINGERDOWN:
533 case SDL_MOUSEBUTTONDOWN:
534 /* Skip this step */
535 SetCurrentBinding(s_iCurrentBinding + 1);
536 break;
537 case SDL_KEYDOWN:
538 if (event.key.keysym.sym == SDLK_BACKSPACE || event.key.keysym.sym == SDLK_AC_BACK) {
539 SetCurrentBinding(s_iCurrentBinding - 1);
540 break;
541 }
542 if (event.key.keysym.sym == SDLK_SPACE) {
543 SetCurrentBinding(s_iCurrentBinding + 1);
544 break;
545 }
546
547 if ((event.key.keysym.sym != SDLK_ESCAPE)) {
548 break;
549 }
550 /* Fall through to signal quit */
551 case SDL_QUIT:
552 done = SDL_TRUE;
553 break;
554 default:
555 break;
556 }
557 }
558
559 SDL_Delay(15);
560
561 /* Wait 100 ms for joystick events to stop coming in,
562 in case a controller sends multiple events for a single control (e.g. axis and button for trigger)
563 */
564 if (s_unPendingAdvanceTime && SDL_GetTicks() - s_unPendingAdvanceTime >= 100) {
565 SetCurrentBinding(s_iCurrentBinding + 1);
566 }
567 }
568
569 if (s_bBindingComplete) {
570 char mapping[1024];
571 char trimmed_name[128];
572 char *spot;
573 int iIndex;
574 char pszElement[12];
575
576 SDL_strlcpy(trimmed_name, name, SDL_arraysize(trimmed_name));
577 while (SDL_isspace(trimmed_name[0])) {
578 SDL_memmove(&trimmed_name[0], &trimmed_name[1], SDL_strlen(trimmed_name));
579 }
580 while (trimmed_name[0] && SDL_isspace(trimmed_name[SDL_strlen(trimmed_name) - 1])) {
581 trimmed_name[SDL_strlen(trimmed_name) - 1] = '\0';
582 }
583 while ((spot = SDL_strchr(trimmed_name, ',')) != NULL) {
584 SDL_memmove(spot, spot + 1, SDL_strlen(spot));
585 }
586
587 /* Initialize mapping with GUID and name */
588 SDL_JoystickGetGUIDString(SDL_JoystickGetGUID(joystick), mapping, SDL_arraysize(mapping));
589 SDL_strlcat(mapping, ",", SDL_arraysize(mapping));
590 SDL_strlcat(mapping, trimmed_name, SDL_arraysize(mapping));
591 SDL_strlcat(mapping, ",", SDL_arraysize(mapping));
592 SDL_strlcat(mapping, "platform:", SDL_arraysize(mapping));
593 SDL_strlcat(mapping, SDL_GetPlatform(), SDL_arraysize(mapping));
594 SDL_strlcat(mapping, ",", SDL_arraysize(mapping));
595
596 for (iIndex = 0; iIndex < SDL_arraysize(s_arrBindings); ++iIndex) {
597 SDL_GameControllerExtendedBind *pBinding = &s_arrBindings[iIndex];
598 if (pBinding->bindType == SDL_CONTROLLER_BINDTYPE_NONE) {
599 continue;
600 }
601
602 if (iIndex < SDL_CONTROLLER_BUTTON_MAX) {
603 SDL_GameControllerButton eButton = (SDL_GameControllerButton)iIndex;
604 SDL_strlcat(mapping, SDL_GameControllerGetStringForButton(eButton), SDL_arraysize(mapping));
605 } else {
606 const char *pszAxisName;
607 switch (iIndex - SDL_CONTROLLER_BUTTON_MAX) {
608 case SDL_CONTROLLER_BINDING_AXIS_LEFTX_NEGATIVE:
609 if (!BMergeAxisBindings(iIndex)) {
610 SDL_strlcat(mapping, "-", SDL_arraysize(mapping));
611 }
612 pszAxisName = SDL_GameControllerGetStringForAxis(SDL_CONTROLLER_AXIS_LEFTX);
613 break;
614 case SDL_CONTROLLER_BINDING_AXIS_LEFTX_POSITIVE:
615 SDL_strlcat(mapping, "+", SDL_arraysize(mapping));
616 pszAxisName = SDL_GameControllerGetStringForAxis(SDL_CONTROLLER_AXIS_LEFTX);
617 break;
618 case SDL_CONTROLLER_BINDING_AXIS_LEFTY_NEGATIVE:
619 if (!BMergeAxisBindings(iIndex)) {
620 SDL_strlcat(mapping, "-", SDL_arraysize(mapping));
621 }
622 pszAxisName = SDL_GameControllerGetStringForAxis(SDL_CONTROLLER_AXIS_LEFTY);
623 break;
624 case SDL_CONTROLLER_BINDING_AXIS_LEFTY_POSITIVE:
625 SDL_strlcat(mapping, "+", SDL_arraysize(mapping));
626 pszAxisName = SDL_GameControllerGetStringForAxis(SDL_CONTROLLER_AXIS_LEFTY);
627 break;
628 case SDL_CONTROLLER_BINDING_AXIS_RIGHTX_NEGATIVE:
629 if (!BMergeAxisBindings(iIndex)) {
630 SDL_strlcat(mapping, "-", SDL_arraysize(mapping));
631 }
632 pszAxisName = SDL_GameControllerGetStringForAxis(SDL_CONTROLLER_AXIS_RIGHTX);
633 break;
634 case SDL_CONTROLLER_BINDING_AXIS_RIGHTX_POSITIVE:
635 SDL_strlcat(mapping, "+", SDL_arraysize(mapping));
636 pszAxisName = SDL_GameControllerGetStringForAxis(SDL_CONTROLLER_AXIS_RIGHTX);
637 break;
638 case SDL_CONTROLLER_BINDING_AXIS_RIGHTY_NEGATIVE:
639 if (!BMergeAxisBindings(iIndex)) {
640 SDL_strlcat(mapping, "-", SDL_arraysize(mapping));
641 }
642 pszAxisName = SDL_GameControllerGetStringForAxis(SDL_CONTROLLER_AXIS_RIGHTY);
643 break;
644 case SDL_CONTROLLER_BINDING_AXIS_RIGHTY_POSITIVE:
645 SDL_strlcat(mapping, "+", SDL_arraysize(mapping));
646 pszAxisName = SDL_GameControllerGetStringForAxis(SDL_CONTROLLER_AXIS_RIGHTY);
647 break;
648 case SDL_CONTROLLER_BINDING_AXIS_TRIGGERLEFT:
649 pszAxisName = SDL_GameControllerGetStringForAxis(SDL_CONTROLLER_AXIS_TRIGGERLEFT);
650 break;
651 case SDL_CONTROLLER_BINDING_AXIS_TRIGGERRIGHT:
652 pszAxisName = SDL_GameControllerGetStringForAxis(SDL_CONTROLLER_AXIS_TRIGGERRIGHT);
653 break;
654 }
655 SDL_strlcat(mapping, pszAxisName, SDL_arraysize(mapping));
656 }
657 SDL_strlcat(mapping, ":", SDL_arraysize(mapping));
658
659 pszElement[0] = '\0';
660 switch (pBinding->bindType) {
661 case SDL_CONTROLLER_BINDTYPE_BUTTON:
662 SDL_snprintf(pszElement, sizeof(pszElement), "b%d", pBinding->value.button);
663 break;
664 case SDL_CONTROLLER_BINDTYPE_AXIS:
665 if (pBinding->value.axis.axis_min == 0 && pBinding->value.axis.axis_max == SDL_JOYSTICK_AXIS_MIN) {
666 /* The negative half axis */
667 SDL_snprintf(pszElement, sizeof(pszElement), "-a%d", pBinding->value.axis.axis);
668 } else if (pBinding->value.axis.axis_min == 0 && pBinding->value.axis.axis_max == SDL_JOYSTICK_AXIS_MAX) {
669 /* The positive half axis */
670 SDL_snprintf(pszElement, sizeof(pszElement), "+a%d", pBinding->value.axis.axis);
671 } else {
672 SDL_snprintf(pszElement, sizeof(pszElement), "a%d", pBinding->value.axis.axis);
673 if (pBinding->value.axis.axis_min > pBinding->value.axis.axis_max) {
674 /* Invert the axis */
675 SDL_strlcat(pszElement, "~", SDL_arraysize(pszElement));
676 }
677 }
678 break;
679 case SDL_CONTROLLER_BINDTYPE_HAT:
680 SDL_snprintf(pszElement, sizeof(pszElement), "h%d.%d", pBinding->value.hat.hat, pBinding->value.hat.hat_mask);
681 break;
682 default:
683 SDL_assert(!"Unknown bind type");
684 break;
685 }
686 SDL_strlcat(mapping, pszElement, SDL_arraysize(mapping));
687 SDL_strlcat(mapping, ",", SDL_arraysize(mapping));
688 }
689
690 SDL_Log("Mapping:\n\n%s\n\n", mapping);
691 /* Print to stdout as well so the user can cat the output somewhere */
692 printf("%s\n", mapping);
693 }
694
695 SDL_free(s_arrAxisState);
696 s_arrAxisState = NULL;
697
698 SDL_DestroyRenderer(screen);
699 }
700
701 int
main(int argc,char * argv[])702 main(int argc, char *argv[])
703 {
704 const char *name;
705 int i;
706 SDL_Joystick *joystick;
707
708 /* Enable standard application logging */
709 SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO);
710
711 /* Initialize SDL (Note: video is required to start event loop) */
712 if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK) < 0) {
713 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't initialize SDL: %s\n", SDL_GetError());
714 exit(1);
715 }
716
717 /* Create a window to display joystick axis position */
718 window = SDL_CreateWindow("Game Controller Map", SDL_WINDOWPOS_CENTERED,
719 SDL_WINDOWPOS_CENTERED, SCREEN_WIDTH,
720 SCREEN_HEIGHT, 0);
721 if (window == NULL) {
722 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create window: %s\n", SDL_GetError());
723 return 2;
724 }
725
726 while (SDL_NumJoysticks() == 0) {
727 SDL_Event event;
728
729 while (SDL_PollEvent(&event) > 0) {
730 switch (event.type) {
731 case SDL_KEYDOWN:
732 if ((event.key.keysym.sym != SDLK_ESCAPE)) {
733 break;
734 }
735 /* Fall through to signal quit */
736 case SDL_QUIT:
737 done = SDL_TRUE;
738 break;
739 default:
740 break;
741 }
742 }
743 }
744
745 /* Print information about the joysticks */
746 SDL_Log("There are %d joysticks attached\n", SDL_NumJoysticks());
747 for (i = 0; i < SDL_NumJoysticks(); ++i) {
748 name = SDL_JoystickNameForIndex(i);
749 SDL_Log("Joystick %d: %s\n", i, name ? name : "Unknown Joystick");
750 joystick = SDL_JoystickOpen(i);
751 if (joystick == NULL) {
752 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "SDL_JoystickOpen(%d) failed: %s\n", i,
753 SDL_GetError());
754 } else {
755 char guid[64];
756 SDL_JoystickGetGUIDString(SDL_JoystickGetGUID(joystick),
757 guid, sizeof (guid));
758 SDL_Log(" axes: %d\n", SDL_JoystickNumAxes(joystick));
759 SDL_Log(" balls: %d\n", SDL_JoystickNumBalls(joystick));
760 SDL_Log(" hats: %d\n", SDL_JoystickNumHats(joystick));
761 SDL_Log(" buttons: %d\n", SDL_JoystickNumButtons(joystick));
762 SDL_Log("instance id: %d\n", SDL_JoystickInstanceID(joystick));
763 SDL_Log(" guid: %s\n", guid);
764 SDL_Log(" VID/PID: 0x%.4x/0x%.4x\n", SDL_JoystickGetVendor(joystick), SDL_JoystickGetProduct(joystick));
765 SDL_JoystickClose(joystick);
766 }
767 }
768
769 joystick = SDL_JoystickOpen(0);
770 if (joystick == NULL) {
771 SDL_Log("Couldn't open joystick 0: %s\n", SDL_GetError());
772 } else {
773 WatchJoystick(joystick);
774 SDL_JoystickClose(joystick);
775 }
776
777 SDL_DestroyWindow(window);
778
779 SDL_QuitSubSystem(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK);
780
781 return 0;
782 }
783
784 #else
785
786 int
main(int argc,char * argv[])787 main(int argc, char *argv[])
788 {
789 SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "SDL compiled without Joystick support.\n");
790 return 1;
791 }
792
793 #endif
794
795 /* vi: set ts=4 sw=4 expandtab: */
796