Browse Source

Fixed handling joysticks that send multiple events for a single control, e.g. both a button and axis event for a trigger.
Tested with the 8Bitdo NES30 Pro on Linux

Sam Lantinga 8 years ago
parent
commit
0c5e7a1067
1 changed files with 304 additions and 190 deletions
  1. 304 190
      test/controllermap.c

+ 304 - 190
test/controllermap.c

@@ -32,16 +32,66 @@
 #define MARKER_BUTTON 1
 #define MARKER_AXIS 2
 
-typedef struct MappingStep
+#define BINDING_COUNT (SDL_CONTROLLER_BUTTON_MAX + SDL_CONTROLLER_AXIS_MAX)
+
+static struct 
 {
     int x, y;
     double angle;
     int marker;
-    char *field;
-    int axis, button, hat, hat_value;
-    char mapping[4096];
-}MappingStep;
 
+} s_arrBindingDisplay[BINDING_COUNT] = {
+    { 387, 167, 0.0, MARKER_BUTTON }, /* SDL_CONTROLLER_BUTTON_A */
+    { 431, 132, 0.0, MARKER_BUTTON }, /* SDL_CONTROLLER_BUTTON_B */
+    { 342, 132, 0.0, MARKER_BUTTON }, /* SDL_CONTROLLER_BUTTON_X */
+    { 389, 101, 0.0, MARKER_BUTTON }, /* SDL_CONTROLLER_BUTTON_Y */
+    { 174, 132, 0.0, MARKER_BUTTON }, /* SDL_CONTROLLER_BUTTON_BACK */
+    { 233, 132, 0.0, MARKER_BUTTON }, /* SDL_CONTROLLER_BUTTON_GUIDE */
+    { 289, 132, 0.0, MARKER_BUTTON }, /* SDL_CONTROLLER_BUTTON_START */
+    {  75, 154, 0.0, MARKER_BUTTON }, /* SDL_CONTROLLER_BUTTON_LEFTSTICK */
+    { 305, 230, 0.0, MARKER_BUTTON }, /* SDL_CONTROLLER_BUTTON_RIGHTSTICK */
+    {  77,  40, 0.0, MARKER_BUTTON }, /* SDL_CONTROLLER_BUTTON_LEFTSHOULDER */
+    { 396,  36, 0.0, MARKER_BUTTON }, /* SDL_CONTROLLER_BUTTON_RIGHTSHOULDER */
+    { 154, 188, 0.0, MARKER_BUTTON }, /* SDL_CONTROLLER_BUTTON_DPAD_UP */
+    { 154, 249, 0.0, MARKER_BUTTON }, /* SDL_CONTROLLER_BUTTON_DPAD_DOWN */
+    { 116, 217, 0.0, MARKER_BUTTON }, /* SDL_CONTROLLER_BUTTON_DPAD_LEFT */
+    { 186, 217, 0.0, MARKER_BUTTON }, /* SDL_CONTROLLER_BUTTON_DPAD_RIGHT */
+    {  75, 154, 0.0,   MARKER_AXIS }, /* SDL_CONTROLLER_AXIS_LEFTX */
+    {  75, 154, 90.0,  MARKER_AXIS }, /* SDL_CONTROLLER_AXIS_LEFTY */
+    { 305, 230, 0.0,   MARKER_AXIS }, /* SDL_CONTROLLER_AXIS_RIGHTX */
+    { 305, 230, 90.0,  MARKER_AXIS }, /* SDL_CONTROLLER_AXIS_RIGHTY */
+    {  91,   0, 90.0,  MARKER_AXIS }, /* SDL_CONTROLLER_AXIS_TRIGGERLEFT */
+    { 375,   0, 90.0,  MARKER_AXIS }, /* SDL_CONTROLLER_AXIS_TRIGGERRIGHT */
+};
+
+static int s_arrBindingOrder[BINDING_COUNT] = {
+    SDL_CONTROLLER_BUTTON_A,
+    SDL_CONTROLLER_BUTTON_B,
+    SDL_CONTROLLER_BUTTON_Y,
+    SDL_CONTROLLER_BUTTON_X,
+    SDL_CONTROLLER_BUTTON_MAX + SDL_CONTROLLER_AXIS_LEFTX,
+    SDL_CONTROLLER_BUTTON_MAX + SDL_CONTROLLER_AXIS_LEFTY,
+    SDL_CONTROLLER_BUTTON_LEFTSTICK,
+    SDL_CONTROLLER_BUTTON_MAX + SDL_CONTROLLER_AXIS_RIGHTX,
+    SDL_CONTROLLER_BUTTON_MAX + SDL_CONTROLLER_AXIS_RIGHTY,
+    SDL_CONTROLLER_BUTTON_RIGHTSTICK,
+    SDL_CONTROLLER_BUTTON_LEFTSHOULDER,
+    SDL_CONTROLLER_BUTTON_MAX + SDL_CONTROLLER_AXIS_TRIGGERLEFT,
+    SDL_CONTROLLER_BUTTON_RIGHTSHOULDER,
+    SDL_CONTROLLER_BUTTON_MAX + SDL_CONTROLLER_AXIS_TRIGGERRIGHT,
+    SDL_CONTROLLER_BUTTON_DPAD_UP,
+    SDL_CONTROLLER_BUTTON_DPAD_RIGHT,
+    SDL_CONTROLLER_BUTTON_DPAD_DOWN,
+    SDL_CONTROLLER_BUTTON_DPAD_LEFT,
+    SDL_CONTROLLER_BUTTON_BACK,
+    SDL_CONTROLLER_BUTTON_GUIDE,
+    SDL_CONTROLLER_BUTTON_START,
+};
+
+static SDL_GameControllerButtonBind s_arrBindings[BINDING_COUNT];
+static int s_iCurrentBinding;
+static Uint32 s_unPendingAdvanceTime;
+static SDL_bool s_bBindingComplete;
 
 SDL_Texture *
 LoadTexture(SDL_Renderer *renderer, const char *file, SDL_bool transparent)
@@ -93,45 +143,99 @@ LoadTexture(SDL_Renderer *renderer, const char *file, SDL_bool transparent)
     return texture;
 }
 
-static SDL_bool
+void SetCurrentBinding(int iBinding)
+{
+    SDL_GameControllerButtonBind *pBinding;
+
+    if (iBinding < 0) {
+        return;
+    }
+
+    if (iBinding == BINDING_COUNT) {
+        s_bBindingComplete = SDL_TRUE;
+        return;
+    }
+
+    s_iCurrentBinding = iBinding;
+
+    pBinding = &s_arrBindings[s_arrBindingOrder[s_iCurrentBinding]];
+    SDL_zerop(pBinding);
+
+    s_unPendingAdvanceTime = 0;
+}
+
+
+static void
+ConfigureBinding(const SDL_GameControllerButtonBind *pBinding)
+{
+    SDL_GameControllerButtonBind *pCurrent;
+    int iIndex;
+    int iCurrentElement = s_arrBindingOrder[s_iCurrentBinding];
+
+    /* Do we already have this binding? */
+    for (iIndex = 0; iIndex < SDL_arraysize(s_arrBindings); ++iIndex) {
+        if (SDL_memcmp(pBinding, &s_arrBindings[iIndex], sizeof(*pBinding)) == 0) {
+            if (iIndex == SDL_CONTROLLER_BUTTON_A && iCurrentElement != SDL_CONTROLLER_BUTTON_B) {
+                /* Skip to the next binding */
+                SetCurrentBinding(s_iCurrentBinding + 1);
+                return;
+            }
+
+            if (iIndex == SDL_CONTROLLER_BUTTON_B) {
+                /* Go back to the previous binding */
+                SetCurrentBinding(s_iCurrentBinding - 1);
+                return;
+            }
+
+            /* Already have this binding, ignore it */
+            return;
+        }
+    }
+
+    /* Should the new binding override the existing one? */
+    pCurrent = &s_arrBindings[iCurrentElement];
+    if (pCurrent->bindType != SDL_CONTROLLER_BINDTYPE_NONE) {
+        SDL_bool bNativeDPad, bCurrentDPad;
+        SDL_bool bNativeAxis, bCurrentAxis;
+        
+        bNativeDPad = (iCurrentElement == SDL_CONTROLLER_BUTTON_DPAD_UP ||
+                       iCurrentElement == SDL_CONTROLLER_BUTTON_DPAD_DOWN ||
+                       iCurrentElement == SDL_CONTROLLER_BUTTON_DPAD_LEFT ||
+                       iCurrentElement == SDL_CONTROLLER_BUTTON_DPAD_RIGHT);
+        bCurrentDPad = (pCurrent->bindType == SDL_CONTROLLER_BINDTYPE_HAT);
+        if (bNativeDPad == bCurrentDPad) {
+            /* We already have a binding of the type we want, ignore the new one */
+            return;
+        }
+
+        bNativeAxis = (iCurrentElement >= SDL_CONTROLLER_BUTTON_MAX);
+        bCurrentAxis = (pCurrent->bindType == SDL_CONTROLLER_BINDTYPE_AXIS);
+        if (bNativeAxis == bCurrentAxis) {
+            /* We already have a binding of the type we want, ignore the new one */
+            return;
+        }
+    }
+
+    *pCurrent = *pBinding;
+
+    s_unPendingAdvanceTime = SDL_GetTicks();
+}
+
+static void
 WatchJoystick(SDL_Joystick * joystick)
 {
     SDL_Window *window = NULL;
     SDL_Renderer *screen = NULL;
     SDL_Texture *background, *button, *axis, *marker;
     const char *name = NULL;
-    SDL_bool retval = SDL_FALSE;
-    SDL_bool done = SDL_FALSE, next=SDL_FALSE;
+    SDL_bool done = SDL_FALSE;
     SDL_Event event;
     SDL_Rect dst;
-    int s, _s;
     Uint8 alpha=200, alpha_step = -1;
     Uint32 alpha_ticks = 0;
-    char mapping[4096], temp[4096];
-    MappingStep *step, *prev_step;
-    MappingStep steps[] = {
-        {342, 132,  0.0,  MARKER_BUTTON, "x", -1, -1, -1, -1, ""},
-        {387, 167,  0.0,  MARKER_BUTTON, "a", -1, -1, -1, -1, ""},
-        {431, 132,  0.0,  MARKER_BUTTON, "b", -1, -1, -1, -1, ""},
-        {389, 101,  0.0,  MARKER_BUTTON, "y", -1, -1, -1, -1, ""},
-        {174, 132,  0.0,  MARKER_BUTTON, "back", -1, -1, -1, -1, ""},
-        {233, 132,  0.0,  MARKER_BUTTON, "guide", -1, -1, -1, -1, ""},
-        {289, 132,  0.0,  MARKER_BUTTON, "start", -1, -1, -1, -1, ""},        
-        {116, 217,  0.0,  MARKER_BUTTON, "dpleft", -1, -1, -1, -1, ""},
-        {154, 249,  0.0,  MARKER_BUTTON, "dpdown", -1, -1, -1, -1, ""},
-        {186, 217,  0.0,  MARKER_BUTTON, "dpright", -1, -1, -1, -1, ""},
-        {154, 188,  0.0,  MARKER_BUTTON, "dpup", -1, -1, -1, -1, ""},
-        {77,  40,   0.0,  MARKER_BUTTON, "leftshoulder", -1, -1, -1, -1, ""},
-        {91, 0,    0.0,  MARKER_BUTTON, "lefttrigger", -1, -1, -1, -1, ""},
-        {396, 36,   0.0,  MARKER_BUTTON, "rightshoulder", -1, -1, -1, -1, ""},
-        {375, 0,    0.0,  MARKER_BUTTON, "righttrigger", -1, -1, -1, -1, ""},
-        {75,  154,  0.0,  MARKER_BUTTON, "leftstick", -1, -1, -1, -1, ""},
-        {305, 230,  0.0,  MARKER_BUTTON, "rightstick", -1, -1, -1, -1, ""},
-        {75,  154,  0.0,  MARKER_AXIS,   "leftx", -1, -1, -1, -1, ""},
-        {75,  154,  90.0, MARKER_AXIS,   "lefty", -1, -1, -1, -1, ""},        
-        {305, 230,  0.0,  MARKER_AXIS,   "rightx", -1, -1, -1, -1, ""},
-        {305, 230,  90.0, MARKER_AXIS,   "righty", -1, -1, -1, -1, ""},
-    };
+    Uint32 bound_ticks = 0;
+    SDL_JoystickID nJoystickID;
+    Uint32 unDeflectedAxes = 0;
 
     /* Create a window to display joystick axis position */
     window = SDL_CreateWindow("Game Controller Map", SDL_WINDOWPOS_CENTERED,
@@ -139,14 +243,14 @@ WatchJoystick(SDL_Joystick * joystick)
                               SCREEN_HEIGHT, 0);
     if (window == NULL) {
         SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create window: %s\n", SDL_GetError());
-        return SDL_FALSE;
+        return;
     }
 
     screen = SDL_CreateRenderer(window, -1, 0);
     if (screen == NULL) {
         SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create renderer: %s\n", SDL_GetError());
         SDL_DestroyWindow(window);
-        return SDL_FALSE;
+        return;
     }
     
     background = LoadTexture(screen, "controllermap.bmp", SDL_FALSE);
@@ -173,23 +277,14 @@ WatchJoystick(SDL_Joystick * joystick)
     To skip a button, press SPACE or click/touch the screen\n\
     To exit, press ESC\n\
     ====================================================================================\n");
-    
-    /* Initialize mapping with GUID and name */
-    SDL_JoystickGetGUIDString(SDL_JoystickGetGUID(joystick), temp, SDL_arraysize(temp));
-    SDL_snprintf(mapping, SDL_arraysize(mapping), "%s,%s,platform:%s,",
-        temp, name ? name : "Unknown Joystick", SDL_GetPlatform());
+
+    nJoystickID = SDL_JoystickInstanceID(joystick);
 
     /* Loop, getting joystick events! */
-    for(s=0; s<SDL_arraysize(steps) && !done;) {
-        /* blank screen, set up for drawing this frame. */
-        step = &steps[s];
-        SDL_strlcpy(step->mapping, mapping, SDL_arraysize(step->mapping));
-        step->axis = -1;
-        step->button = -1;
-        step->hat = -1;
-        step->hat_value = -1;
-        
-        switch(step->marker) {
+    while (!done && !s_bBindingComplete) {
+        int iElement = s_arrBindingOrder[s_iCurrentBinding];
+
+        switch (s_arrBindingDisplay[iElement].marker) {
             case MARKER_AXIS:
                 marker = axis;
                 break;
@@ -200,137 +295,182 @@ WatchJoystick(SDL_Joystick * joystick)
                 break;
         }
         
-        dst.x = step->x;
-        dst.y = step->y;
+        dst.x = s_arrBindingDisplay[iElement].x;
+        dst.y = s_arrBindingDisplay[iElement].y;
         SDL_QueryTexture(marker, NULL, NULL, &dst.w, &dst.h);
-        next=SDL_FALSE;
-
-        SDL_SetRenderDrawColor(screen, 0xFF, 0xFF, 0xFF, SDL_ALPHA_OPAQUE);
 
-        while (!done && !next) {
-            if (SDL_GetTicks() - alpha_ticks > 5) {
-                alpha_ticks = SDL_GetTicks();
-                alpha += alpha_step;
-                if (alpha == 255) {
-                    alpha_step = -1;
-                }
-                if (alpha < 128) {
-                    alpha_step = 1;
-                }
+        if (SDL_GetTicks() - alpha_ticks > 5) {
+            alpha_ticks = SDL_GetTicks();
+            alpha += alpha_step;
+            if (alpha == 255) {
+                alpha_step = -1;
             }
+            if (alpha < 128) {
+                alpha_step = 1;
+            }
+        }
+
+        SDL_SetRenderDrawColor(screen, 0xFF, 0xFF, 0xFF, SDL_ALPHA_OPAQUE);
+        SDL_RenderClear(screen);
+        SDL_RenderCopy(screen, background, NULL, NULL);
+        SDL_SetTextureAlphaMod(marker, alpha);
+        SDL_SetTextureColorMod(marker, 10, 255, 21);
+        SDL_RenderCopyEx(screen, marker, NULL, &dst, s_arrBindingDisplay[iElement].angle, NULL, SDL_FLIP_NONE);
+        SDL_RenderPresent(screen);
             
-            SDL_RenderClear(screen);
-            SDL_RenderCopy(screen, background, NULL, NULL);
-            SDL_SetTextureAlphaMod(marker, alpha);
-            SDL_SetTextureColorMod(marker, 10, 255, 21);
-            SDL_RenderCopyEx(screen, marker, NULL, &dst, step->angle, NULL, SDL_FLIP_NONE);
-            SDL_RenderPresent(screen);
-            
-            if (SDL_PollEvent(&event)) {
-                switch (event.type) {
-                case SDL_JOYAXISMOTION:
-                    if ((event.jaxis.value > 20000 || event.jaxis.value < -20000) && event.jaxis.value != -32768) {
-                        for (_s = 0; _s < s; _s++) {
-                            if (steps[_s].axis == event.jaxis.axis) {
-                                break;
-                            }
-                        }
-                        if (_s == s) {
-                            step->axis = event.jaxis.axis;
-                            SDL_strlcat(mapping, step->field, SDL_arraysize(mapping));
-                            SDL_snprintf(temp, SDL_arraysize(temp), ":a%u,", event.jaxis.axis);
-                            SDL_strlcat(mapping, temp, SDL_arraysize(mapping));
-                            s++;
-                            next=SDL_TRUE;
-                        }
+        while (SDL_PollEvent(&event) > 0) {
+            switch (event.type) {
+            case SDL_JOYDEVICEREMOVED:
+                if (event.jaxis.which == nJoystickID) {
+                    done = SDL_TRUE;
+                }
+                break;
+            case SDL_JOYAXISMOTION:
+                if (event.jaxis.which == nJoystickID) {
+                    uint32_t unAxisMask = (1 << event.jaxis.axis);
+                    SDL_bool bDeflected = (event.jaxis.value <= -20000 || event.jaxis.value >= 20000);
+                    if (bDeflected && !(unDeflectedAxes & unAxisMask)) {
+                        SDL_GameControllerButtonBind binding;
+                        SDL_zero(binding);
+                        binding.bindType = SDL_CONTROLLER_BINDTYPE_AXIS;
+                        binding.value.axis = event.jaxis.axis;
+                        ConfigureBinding(&binding);
                     }
-                    
-                    break;
-                case SDL_JOYHATMOTION:
-                        if (event.jhat.value == SDL_HAT_CENTERED) {
-                            break;  /* ignore centering, we're probably just coming back to the center from the previous item we set. */
-                        }
-                        for (_s = 0; _s < s; _s++) {
-                            if (steps[_s].hat == event.jhat.hat && steps[_s].hat_value == event.jhat.value) {
-                                break;
-                            }
-                        }
-                        if (_s == s) {
-                            step->hat = event.jhat.hat;
-                            step->hat_value = event.jhat.value;
-                            SDL_strlcat(mapping, step->field, SDL_arraysize(mapping));
-                            SDL_snprintf(temp, SDL_arraysize(temp), ":h%u.%u,", event.jhat.hat, event.jhat.value );
-                            SDL_strlcat(mapping, temp, SDL_arraysize(mapping));
-                            s++;
-                            next=SDL_TRUE;
-                        }
-                    break;
-                case SDL_JOYBALLMOTION:
-                    break;
-                case SDL_JOYBUTTONUP:
-                    for (_s = 0; _s < s; _s++) {
-                        if (steps[_s].button == event.jbutton.button) {
-                            break;
-                        }
+                    if (bDeflected) {
+                        unDeflectedAxes |= unAxisMask;
+                    } else {
+                        unDeflectedAxes &= ~unAxisMask;
                     }
-                    if (_s == s) {
-                        step->button = event.jbutton.button;
-                        SDL_strlcat(mapping, step->field, SDL_arraysize(mapping));
-                        SDL_snprintf(temp, SDL_arraysize(temp), ":b%u,", event.jbutton.button);
-                        SDL_strlcat(mapping, temp, SDL_arraysize(mapping));
-                        s++;
-                        next=SDL_TRUE;
+                }
+                break;
+            case SDL_JOYHATMOTION:
+                if (event.jhat.which == nJoystickID) {
+                    if (event.jhat.value != SDL_HAT_CENTERED) {
+                        SDL_GameControllerButtonBind binding;
+                        SDL_zero(binding);
+                        binding.bindType = SDL_CONTROLLER_BINDTYPE_HAT;
+                        binding.value.hat.hat = event.jhat.hat;
+                        binding.value.hat.hat_mask = event.jhat.value;
+                        ConfigureBinding(&binding);
                     }
+                }
+                break;
+            case SDL_JOYBALLMOTION:
+                break;
+            case SDL_JOYBUTTONDOWN:
+                if (event.jbutton.which == nJoystickID) {
+                    SDL_GameControllerButtonBind binding;
+                    SDL_zero(binding);
+                    binding.bindType = SDL_CONTROLLER_BINDTYPE_BUTTON;
+                    binding.value.button = event.jbutton.button;
+                    ConfigureBinding(&binding);
+                }
+                break;
+            case SDL_FINGERDOWN:
+            case SDL_MOUSEBUTTONDOWN:
+                /* Skip this step */
+                SetCurrentBinding(s_iCurrentBinding + 1);
+                break;
+            case SDL_KEYDOWN:
+                if (event.key.keysym.sym == SDLK_BACKSPACE || event.key.keysym.sym == SDLK_AC_BACK) {
+                    SetCurrentBinding(s_iCurrentBinding - 1);
                     break;
-                case SDL_FINGERDOWN:
-                case SDL_MOUSEBUTTONDOWN:
-                    /* Skip this step */
-                    s++;
-                    next=SDL_TRUE;
-                    break;
-                case SDL_KEYDOWN:
-                    if (event.key.keysym.sym == SDLK_BACKSPACE || event.key.keysym.sym == SDLK_AC_BACK) {
-                        /* Undo! */
-                        if (s > 0) {
-                            prev_step = &steps[--s];
-                            SDL_strlcpy(mapping, prev_step->mapping, SDL_arraysize(prev_step->mapping));
-                            next = SDL_TRUE;
-                        }
-                        break;
-                    }
-                    if (event.key.keysym.sym == SDLK_SPACE) {
-                        /* Skip this step */
-                        s++;
-                        next=SDL_TRUE;
-                        break;
-                    }
-                    
-                    if ((event.key.keysym.sym != SDLK_ESCAPE)) {
-                        break;
-                    }
-                    /* Fall through to signal quit */
-                case SDL_QUIT:
-                    done = SDL_TRUE;
+                }
+                if (event.key.keysym.sym == SDLK_SPACE) {
+                    SetCurrentBinding(s_iCurrentBinding + 1);
                     break;
-                default:
+                }
+
+                if ((event.key.keysym.sym != SDLK_ESCAPE)) {
                     break;
                 }
+                /* Fall through to signal quit */
+            case SDL_QUIT:
+                done = SDL_TRUE;
+                break;
+            default:
+                break;
             }
         }
 
+        SDL_Delay(15);
+
+        /* Wait 100 ms for joystick events to stop coming in,
+           in case a controller sends multiple events for a single control (e.g. axis and button for trigger)
+        */
+        if (s_unPendingAdvanceTime && SDL_GetTicks() - s_unPendingAdvanceTime >= 100) {
+            SetCurrentBinding(s_iCurrentBinding + 1);
+        }
     }
 
-    if (s == SDL_arraysize(steps) ) {
+    if (s_bBindingComplete) {
+        char mapping[1024];
+        char trimmed_name[128];
+        char *spot;
+        int iIndex;
+        char pszElement[12];
+
+        SDL_strlcpy(trimmed_name, name, SDL_arraysize(trimmed_name));
+        while (SDL_isspace(trimmed_name[0])) {
+            SDL_memmove(&trimmed_name[0], &trimmed_name[1], SDL_strlen(trimmed_name));
+        }
+        while (trimmed_name[0] && SDL_isspace(trimmed_name[SDL_strlen(trimmed_name) - 1])) {
+            trimmed_name[SDL_strlen(trimmed_name) - 1] = '\0';
+        }
+        while ((spot = SDL_strchr(trimmed_name, ',')) != NULL) {
+            SDL_memmove(spot, spot + 1, SDL_strlen(spot));
+        }
+
+        /* Initialize mapping with GUID and name */
+        SDL_JoystickGetGUIDString(SDL_JoystickGetGUID(joystick), mapping, SDL_arraysize(mapping));
+        SDL_strlcat(mapping, ",", SDL_arraysize(mapping));
+        SDL_strlcat(mapping, trimmed_name, SDL_arraysize(mapping));
+        SDL_strlcat(mapping, ",", SDL_arraysize(mapping));
+        SDL_strlcat(mapping, "platform:", SDL_arraysize(mapping));
+        SDL_strlcat(mapping, SDL_GetPlatform(), SDL_arraysize(mapping));
+        SDL_strlcat(mapping, ",", SDL_arraysize(mapping));
+
+        for (iIndex = 0; iIndex < SDL_arraysize(s_arrBindings); ++iIndex) {
+            SDL_GameControllerButtonBind *pBinding = &s_arrBindings[iIndex];
+            if (pBinding->bindType == SDL_CONTROLLER_BINDTYPE_NONE) {
+                continue;
+            }
+
+            if (iIndex < SDL_CONTROLLER_BUTTON_MAX) {
+                SDL_GameControllerButton eButton = (SDL_GameControllerButton)iIndex;
+                SDL_strlcat(mapping, SDL_GameControllerGetStringForButton(eButton), SDL_arraysize(mapping));
+            } else {
+                SDL_GameControllerAxis eAxis = (SDL_GameControllerAxis)(iIndex - SDL_CONTROLLER_BUTTON_MAX);
+                SDL_strlcat(mapping, SDL_GameControllerGetStringForAxis(eAxis), SDL_arraysize(mapping));
+            }
+            SDL_strlcat(mapping, ":", SDL_arraysize(mapping));
+
+            pszElement[0] = '\0';
+            switch (pBinding->bindType) {
+            case SDL_CONTROLLER_BINDTYPE_BUTTON:
+                SDL_snprintf(pszElement, sizeof(pszElement), "b%d", pBinding->value.button);
+                break;
+            case SDL_CONTROLLER_BINDTYPE_AXIS:
+                SDL_snprintf(pszElement, sizeof(pszElement), "a%d", pBinding->value.axis);
+                break;
+            case SDL_CONTROLLER_BINDTYPE_HAT:
+                SDL_snprintf(pszElement, sizeof(pszElement), "h%d.%d", pBinding->value.hat.hat, pBinding->value.hat.hat_mask);
+                break;
+            default:
+                SDL_assert(!"Unknown bind type");
+                break;
+            }
+            SDL_strlcat(mapping, pszElement, SDL_arraysize(mapping));
+            SDL_strlcat(mapping, ",", SDL_arraysize(mapping));
+        }
+
         SDL_Log("Mapping:\n\n%s\n\n", mapping);
         /* Print to stdout as well so the user can cat the output somewhere */
         printf("%s\n", mapping);
     }
     
-    while(SDL_PollEvent(&event)) {};
-    
     SDL_DestroyRenderer(screen);
     SDL_DestroyWindow(window);
-    return retval;
 }
 
 int
@@ -378,9 +518,6 @@ main(int argc, char *argv[])
 #else
     if (argv[1]) {
 #endif
-        SDL_bool reportederror = SDL_FALSE;
-        SDL_bool keepGoing = SDL_TRUE;
-        SDL_Event event;
         int device;
 #ifdef __ANDROID__
         device = 0;
@@ -388,34 +525,11 @@ main(int argc, char *argv[])
         device = atoi(argv[1]);
 #endif
         joystick = SDL_JoystickOpen(device);
-
-        while ( keepGoing ) {
-            if (joystick == NULL) {
-                if ( !reportederror ) {
-                    SDL_Log("Couldn't open joystick %d: %s\n", device, SDL_GetError());
-                    keepGoing = SDL_FALSE;
-                    reportederror = SDL_TRUE;
-                }
-            } else {
-                reportederror = SDL_FALSE;
-                keepGoing = WatchJoystick(joystick);
-                SDL_JoystickClose(joystick);
-            }
-
-            joystick = NULL;
-            if (keepGoing) {
-                SDL_Log("Waiting for attach\n");
-            }
-            while (keepGoing) {
-                SDL_WaitEvent(&event);
-                if ((event.type == SDL_QUIT) || (event.type == SDL_FINGERDOWN)
-                    || (event.type == SDL_MOUSEBUTTONDOWN)) {
-                    keepGoing = SDL_FALSE;
-                } else if (event.type == SDL_JOYDEVICEADDED) {
-                    joystick = SDL_JoystickOpen(device);
-                    break;
-                }
-            }
+        if (joystick == NULL) {
+            SDL_Log("Couldn't open joystick %d: %s\n", device, SDL_GetError());
+        } else {
+            WatchJoystick(joystick);
+            SDL_JoystickClose(joystick);
         }
     }
     else {