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