test/controllermap.c
author Sam Lantinga <slouken@libsdl.org>
Tue, 03 Jan 2017 23:39:28 -0800
changeset 10745 7461fcef6ae2
parent 10737 3406a0f8b041
child 10747 da1f4eeac65a
permissions -rw-r--r--
Fixed binding the D-pad on some Super NES style controllers
Fixed a case where partial trigger pull could be bound to another button

There is a fundamental problem not resolved by this commit:

Some controllers have axes (triggers, pedals, etc.) that don't start at zero, but we're guaranteed that if we get a value that it's correct. For these controllers, the current code works, where we take the first value we get and use that as the zero point and generate axis motion starting from that point on.

Other controllers have digital axes (D-pad) that assume a zero starting point, and the first value we get is the min or max axis value when the D-pad is moved. For these controllers, the current code thinks that the zero point is the axis value after the D-pad motion and this doesn't work.

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