test/controllermap.c
author Sam Lantinga <slouken@libsdl.org>
Thu, 15 Dec 2016 14:27:22 -0800
changeset 10705 997239dcb197
parent 10597 7b2a0f5a4380
child 10706 71ce5d7d2610
permissions -rw-r--r--
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
     1 /*
     2   Copyright (C) 1997-2016 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 #define BINDING_COUNT (SDL_CONTROLLER_BUTTON_MAX + SDL_CONTROLLER_AXIS_MAX)
    36 
    37 static struct 
    38 {
    39     int x, y;
    40     double angle;
    41     int marker;
    42 
    43 } s_arrBindingDisplay[BINDING_COUNT] = {
    44     { 387, 167, 0.0, MARKER_BUTTON }, /* SDL_CONTROLLER_BUTTON_A */
    45     { 431, 132, 0.0, MARKER_BUTTON }, /* SDL_CONTROLLER_BUTTON_B */
    46     { 342, 132, 0.0, MARKER_BUTTON }, /* SDL_CONTROLLER_BUTTON_X */
    47     { 389, 101, 0.0, MARKER_BUTTON }, /* SDL_CONTROLLER_BUTTON_Y */
    48     { 174, 132, 0.0, MARKER_BUTTON }, /* SDL_CONTROLLER_BUTTON_BACK */
    49     { 233, 132, 0.0, MARKER_BUTTON }, /* SDL_CONTROLLER_BUTTON_GUIDE */
    50     { 289, 132, 0.0, MARKER_BUTTON }, /* SDL_CONTROLLER_BUTTON_START */
    51     {  75, 154, 0.0, MARKER_BUTTON }, /* SDL_CONTROLLER_BUTTON_LEFTSTICK */
    52     { 305, 230, 0.0, MARKER_BUTTON }, /* SDL_CONTROLLER_BUTTON_RIGHTSTICK */
    53     {  77,  40, 0.0, MARKER_BUTTON }, /* SDL_CONTROLLER_BUTTON_LEFTSHOULDER */
    54     { 396,  36, 0.0, MARKER_BUTTON }, /* SDL_CONTROLLER_BUTTON_RIGHTSHOULDER */
    55     { 154, 188, 0.0, MARKER_BUTTON }, /* SDL_CONTROLLER_BUTTON_DPAD_UP */
    56     { 154, 249, 0.0, MARKER_BUTTON }, /* SDL_CONTROLLER_BUTTON_DPAD_DOWN */
    57     { 116, 217, 0.0, MARKER_BUTTON }, /* SDL_CONTROLLER_BUTTON_DPAD_LEFT */
    58     { 186, 217, 0.0, MARKER_BUTTON }, /* SDL_CONTROLLER_BUTTON_DPAD_RIGHT */
    59     {  75, 154, 0.0,   MARKER_AXIS }, /* SDL_CONTROLLER_AXIS_LEFTX */
    60     {  75, 154, 90.0,  MARKER_AXIS }, /* SDL_CONTROLLER_AXIS_LEFTY */
    61     { 305, 230, 0.0,   MARKER_AXIS }, /* SDL_CONTROLLER_AXIS_RIGHTX */
    62     { 305, 230, 90.0,  MARKER_AXIS }, /* SDL_CONTROLLER_AXIS_RIGHTY */
    63     {  91,   0, 90.0,  MARKER_AXIS }, /* SDL_CONTROLLER_AXIS_TRIGGERLEFT */
    64     { 375,   0, 90.0,  MARKER_AXIS }, /* SDL_CONTROLLER_AXIS_TRIGGERRIGHT */
    65 };
    66 
    67 static int s_arrBindingOrder[BINDING_COUNT] = {
    68     SDL_CONTROLLER_BUTTON_A,
    69     SDL_CONTROLLER_BUTTON_B,
    70     SDL_CONTROLLER_BUTTON_Y,
    71     SDL_CONTROLLER_BUTTON_X,
    72     SDL_CONTROLLER_BUTTON_MAX + SDL_CONTROLLER_AXIS_LEFTX,
    73     SDL_CONTROLLER_BUTTON_MAX + SDL_CONTROLLER_AXIS_LEFTY,
    74     SDL_CONTROLLER_BUTTON_LEFTSTICK,
    75     SDL_CONTROLLER_BUTTON_MAX + SDL_CONTROLLER_AXIS_RIGHTX,
    76     SDL_CONTROLLER_BUTTON_MAX + SDL_CONTROLLER_AXIS_RIGHTY,
    77     SDL_CONTROLLER_BUTTON_RIGHTSTICK,
    78     SDL_CONTROLLER_BUTTON_LEFTSHOULDER,
    79     SDL_CONTROLLER_BUTTON_MAX + SDL_CONTROLLER_AXIS_TRIGGERLEFT,
    80     SDL_CONTROLLER_BUTTON_RIGHTSHOULDER,
    81     SDL_CONTROLLER_BUTTON_MAX + SDL_CONTROLLER_AXIS_TRIGGERRIGHT,
    82     SDL_CONTROLLER_BUTTON_DPAD_UP,
    83     SDL_CONTROLLER_BUTTON_DPAD_RIGHT,
    84     SDL_CONTROLLER_BUTTON_DPAD_DOWN,
    85     SDL_CONTROLLER_BUTTON_DPAD_LEFT,
    86     SDL_CONTROLLER_BUTTON_BACK,
    87     SDL_CONTROLLER_BUTTON_GUIDE,
    88     SDL_CONTROLLER_BUTTON_START,
    89 };
    90 
    91 static SDL_GameControllerButtonBind s_arrBindings[BINDING_COUNT];
    92 static int s_iCurrentBinding;
    93 static Uint32 s_unPendingAdvanceTime;
    94 static SDL_bool s_bBindingComplete;
    95 
    96 SDL_Texture *
    97 LoadTexture(SDL_Renderer *renderer, const char *file, SDL_bool transparent)
    98 {
    99     SDL_Surface *temp;
   100     SDL_Texture *texture;
   101 
   102     /* Load the sprite image */
   103     temp = SDL_LoadBMP(file);
   104     if (temp == NULL) {
   105         SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't load %s: %s", file, SDL_GetError());
   106         return NULL;
   107     }
   108 
   109     /* Set transparent pixel as the pixel at (0,0) */
   110     if (transparent) {
   111         if (temp->format->palette) {
   112             SDL_SetColorKey(temp, SDL_TRUE, *(Uint8 *) temp->pixels);
   113         } else {
   114             switch (temp->format->BitsPerPixel) {
   115             case 15:
   116                 SDL_SetColorKey(temp, SDL_TRUE,
   117                                 (*(Uint16 *) temp->pixels) & 0x00007FFF);
   118                 break;
   119             case 16:
   120                 SDL_SetColorKey(temp, SDL_TRUE, *(Uint16 *) temp->pixels);
   121                 break;
   122             case 24:
   123                 SDL_SetColorKey(temp, SDL_TRUE,
   124                                 (*(Uint32 *) temp->pixels) & 0x00FFFFFF);
   125                 break;
   126             case 32:
   127                 SDL_SetColorKey(temp, SDL_TRUE, *(Uint32 *) temp->pixels);
   128                 break;
   129             }
   130         }
   131     }
   132 
   133     /* Create textures from the image */
   134     texture = SDL_CreateTextureFromSurface(renderer, temp);
   135     if (!texture) {
   136         SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create texture: %s\n", SDL_GetError());
   137         SDL_FreeSurface(temp);
   138         return NULL;
   139     }
   140     SDL_FreeSurface(temp);
   141 
   142     /* We're ready to roll. :) */
   143     return texture;
   144 }
   145 
   146 void SetCurrentBinding(int iBinding)
   147 {
   148     SDL_GameControllerButtonBind *pBinding;
   149 
   150     if (iBinding < 0) {
   151         return;
   152     }
   153 
   154     if (iBinding == BINDING_COUNT) {
   155         s_bBindingComplete = SDL_TRUE;
   156         return;
   157     }
   158 
   159     s_iCurrentBinding = iBinding;
   160 
   161     pBinding = &s_arrBindings[s_arrBindingOrder[s_iCurrentBinding]];
   162     SDL_zerop(pBinding);
   163 
   164     s_unPendingAdvanceTime = 0;
   165 }
   166 
   167 
   168 static void
   169 ConfigureBinding(const SDL_GameControllerButtonBind *pBinding)
   170 {
   171     SDL_GameControllerButtonBind *pCurrent;
   172     int iIndex;
   173     int iCurrentElement = s_arrBindingOrder[s_iCurrentBinding];
   174 
   175     /* Do we already have this binding? */
   176     for (iIndex = 0; iIndex < SDL_arraysize(s_arrBindings); ++iIndex) {
   177         if (SDL_memcmp(pBinding, &s_arrBindings[iIndex], sizeof(*pBinding)) == 0) {
   178             if (iIndex == SDL_CONTROLLER_BUTTON_A && iCurrentElement != SDL_CONTROLLER_BUTTON_B) {
   179                 /* Skip to the next binding */
   180                 SetCurrentBinding(s_iCurrentBinding + 1);
   181                 return;
   182             }
   183 
   184             if (iIndex == SDL_CONTROLLER_BUTTON_B) {
   185                 /* Go back to the previous binding */
   186                 SetCurrentBinding(s_iCurrentBinding - 1);
   187                 return;
   188             }
   189 
   190             /* Already have this binding, ignore it */
   191             return;
   192         }
   193     }
   194 
   195     /* Should the new binding override the existing one? */
   196     pCurrent = &s_arrBindings[iCurrentElement];
   197     if (pCurrent->bindType != SDL_CONTROLLER_BINDTYPE_NONE) {
   198         SDL_bool bNativeDPad, bCurrentDPad;
   199         SDL_bool bNativeAxis, bCurrentAxis;
   200         
   201         bNativeDPad = (iCurrentElement == SDL_CONTROLLER_BUTTON_DPAD_UP ||
   202                        iCurrentElement == SDL_CONTROLLER_BUTTON_DPAD_DOWN ||
   203                        iCurrentElement == SDL_CONTROLLER_BUTTON_DPAD_LEFT ||
   204                        iCurrentElement == SDL_CONTROLLER_BUTTON_DPAD_RIGHT);
   205         bCurrentDPad = (pCurrent->bindType == SDL_CONTROLLER_BINDTYPE_HAT);
   206         if (bNativeDPad == bCurrentDPad) {
   207             /* We already have a binding of the type we want, ignore the new one */
   208             return;
   209         }
   210 
   211         bNativeAxis = (iCurrentElement >= SDL_CONTROLLER_BUTTON_MAX);
   212         bCurrentAxis = (pCurrent->bindType == SDL_CONTROLLER_BINDTYPE_AXIS);
   213         if (bNativeAxis == bCurrentAxis) {
   214             /* We already have a binding of the type we want, ignore the new one */
   215             return;
   216         }
   217     }
   218 
   219     *pCurrent = *pBinding;
   220 
   221     s_unPendingAdvanceTime = SDL_GetTicks();
   222 }
   223 
   224 static void
   225 WatchJoystick(SDL_Joystick * joystick)
   226 {
   227     SDL_Window *window = NULL;
   228     SDL_Renderer *screen = NULL;
   229     SDL_Texture *background, *button, *axis, *marker;
   230     const char *name = NULL;
   231     SDL_bool done = SDL_FALSE;
   232     SDL_Event event;
   233     SDL_Rect dst;
   234     Uint8 alpha=200, alpha_step = -1;
   235     Uint32 alpha_ticks = 0;
   236     Uint32 bound_ticks = 0;
   237     SDL_JoystickID nJoystickID;
   238     Uint32 unDeflectedAxes = 0;
   239 
   240     /* Create a window to display joystick axis position */
   241     window = SDL_CreateWindow("Game Controller Map", SDL_WINDOWPOS_CENTERED,
   242                               SDL_WINDOWPOS_CENTERED, SCREEN_WIDTH,
   243                               SCREEN_HEIGHT, 0);
   244     if (window == NULL) {
   245         SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create window: %s\n", SDL_GetError());
   246         return;
   247     }
   248 
   249     screen = SDL_CreateRenderer(window, -1, 0);
   250     if (screen == NULL) {
   251         SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create renderer: %s\n", SDL_GetError());
   252         SDL_DestroyWindow(window);
   253         return;
   254     }
   255     
   256     background = LoadTexture(screen, "controllermap.bmp", SDL_FALSE);
   257     button = LoadTexture(screen, "button.bmp", SDL_TRUE);
   258     axis = LoadTexture(screen, "axis.bmp", SDL_TRUE);
   259     SDL_RaiseWindow(window);
   260 
   261     /* scale for platforms that don't give you the window size you asked for. */
   262     SDL_RenderSetLogicalSize(screen, SCREEN_WIDTH, SCREEN_HEIGHT);
   263 
   264     /* Print info about the joystick we are watching */
   265     name = SDL_JoystickName(joystick);
   266     SDL_Log("Watching joystick %d: (%s)\n", SDL_JoystickInstanceID(joystick),
   267            name ? name : "Unknown Joystick");
   268     SDL_Log("Joystick has %d axes, %d hats, %d balls, and %d buttons\n",
   269            SDL_JoystickNumAxes(joystick), SDL_JoystickNumHats(joystick),
   270            SDL_JoystickNumBalls(joystick), SDL_JoystickNumButtons(joystick));
   271     
   272     SDL_Log("\n\n\
   273     ====================================================================================\n\
   274     Press the buttons on your controller when indicated\n\
   275     (Your controller may look different than the picture)\n\
   276     If you want to correct a mistake, press backspace or the back button on your device\n\
   277     To skip a button, press SPACE or click/touch the screen\n\
   278     To exit, press ESC\n\
   279     ====================================================================================\n");
   280 
   281     nJoystickID = SDL_JoystickInstanceID(joystick);
   282 
   283     /* Loop, getting joystick events! */
   284     while (!done && !s_bBindingComplete) {
   285         int iElement = s_arrBindingOrder[s_iCurrentBinding];
   286 
   287         switch (s_arrBindingDisplay[iElement].marker) {
   288             case MARKER_AXIS:
   289                 marker = axis;
   290                 break;
   291             case MARKER_BUTTON:
   292                 marker = button;
   293                 break;
   294             default:
   295                 break;
   296         }
   297         
   298         dst.x = s_arrBindingDisplay[iElement].x;
   299         dst.y = s_arrBindingDisplay[iElement].y;
   300         SDL_QueryTexture(marker, NULL, NULL, &dst.w, &dst.h);
   301 
   302         if (SDL_GetTicks() - alpha_ticks > 5) {
   303             alpha_ticks = SDL_GetTicks();
   304             alpha += alpha_step;
   305             if (alpha == 255) {
   306                 alpha_step = -1;
   307             }
   308             if (alpha < 128) {
   309                 alpha_step = 1;
   310             }
   311         }
   312 
   313         SDL_SetRenderDrawColor(screen, 0xFF, 0xFF, 0xFF, SDL_ALPHA_OPAQUE);
   314         SDL_RenderClear(screen);
   315         SDL_RenderCopy(screen, background, NULL, NULL);
   316         SDL_SetTextureAlphaMod(marker, alpha);
   317         SDL_SetTextureColorMod(marker, 10, 255, 21);
   318         SDL_RenderCopyEx(screen, marker, NULL, &dst, s_arrBindingDisplay[iElement].angle, NULL, SDL_FLIP_NONE);
   319         SDL_RenderPresent(screen);
   320             
   321         while (SDL_PollEvent(&event) > 0) {
   322             switch (event.type) {
   323             case SDL_JOYDEVICEREMOVED:
   324                 if (event.jaxis.which == nJoystickID) {
   325                     done = SDL_TRUE;
   326                 }
   327                 break;
   328             case SDL_JOYAXISMOTION:
   329                 if (event.jaxis.which == nJoystickID) {
   330                     uint32_t unAxisMask = (1 << event.jaxis.axis);
   331                     SDL_bool bDeflected = (event.jaxis.value <= -20000 || event.jaxis.value >= 20000);
   332                     if (bDeflected && !(unDeflectedAxes & unAxisMask)) {
   333                         SDL_GameControllerButtonBind binding;
   334                         SDL_zero(binding);
   335                         binding.bindType = SDL_CONTROLLER_BINDTYPE_AXIS;
   336                         binding.value.axis = event.jaxis.axis;
   337                         ConfigureBinding(&binding);
   338                     }
   339                     if (bDeflected) {
   340                         unDeflectedAxes |= unAxisMask;
   341                     } else {
   342                         unDeflectedAxes &= ~unAxisMask;
   343                     }
   344                 }
   345                 break;
   346             case SDL_JOYHATMOTION:
   347                 if (event.jhat.which == nJoystickID) {
   348                     if (event.jhat.value != SDL_HAT_CENTERED) {
   349                         SDL_GameControllerButtonBind binding;
   350                         SDL_zero(binding);
   351                         binding.bindType = SDL_CONTROLLER_BINDTYPE_HAT;
   352                         binding.value.hat.hat = event.jhat.hat;
   353                         binding.value.hat.hat_mask = event.jhat.value;
   354                         ConfigureBinding(&binding);
   355                     }
   356                 }
   357                 break;
   358             case SDL_JOYBALLMOTION:
   359                 break;
   360             case SDL_JOYBUTTONDOWN:
   361                 if (event.jbutton.which == nJoystickID) {
   362                     SDL_GameControllerButtonBind binding;
   363                     SDL_zero(binding);
   364                     binding.bindType = SDL_CONTROLLER_BINDTYPE_BUTTON;
   365                     binding.value.button = event.jbutton.button;
   366                     ConfigureBinding(&binding);
   367                 }
   368                 break;
   369             case SDL_FINGERDOWN:
   370             case SDL_MOUSEBUTTONDOWN:
   371                 /* Skip this step */
   372                 SetCurrentBinding(s_iCurrentBinding + 1);
   373                 break;
   374             case SDL_KEYDOWN:
   375                 if (event.key.keysym.sym == SDLK_BACKSPACE || event.key.keysym.sym == SDLK_AC_BACK) {
   376                     SetCurrentBinding(s_iCurrentBinding - 1);
   377                     break;
   378                 }
   379                 if (event.key.keysym.sym == SDLK_SPACE) {
   380                     SetCurrentBinding(s_iCurrentBinding + 1);
   381                     break;
   382                 }
   383 
   384                 if ((event.key.keysym.sym != SDLK_ESCAPE)) {
   385                     break;
   386                 }
   387                 /* Fall through to signal quit */
   388             case SDL_QUIT:
   389                 done = SDL_TRUE;
   390                 break;
   391             default:
   392                 break;
   393             }
   394         }
   395 
   396         SDL_Delay(15);
   397 
   398         /* Wait 100 ms for joystick events to stop coming in,
   399            in case a controller sends multiple events for a single control (e.g. axis and button for trigger)
   400         */
   401         if (s_unPendingAdvanceTime && SDL_GetTicks() - s_unPendingAdvanceTime >= 100) {
   402             SetCurrentBinding(s_iCurrentBinding + 1);
   403         }
   404     }
   405 
   406     if (s_bBindingComplete) {
   407         char mapping[1024];
   408         char trimmed_name[128];
   409         char *spot;
   410         int iIndex;
   411         char pszElement[12];
   412 
   413         SDL_strlcpy(trimmed_name, name, SDL_arraysize(trimmed_name));
   414         while (SDL_isspace(trimmed_name[0])) {
   415             SDL_memmove(&trimmed_name[0], &trimmed_name[1], SDL_strlen(trimmed_name));
   416         }
   417         while (trimmed_name[0] && SDL_isspace(trimmed_name[SDL_strlen(trimmed_name) - 1])) {
   418             trimmed_name[SDL_strlen(trimmed_name) - 1] = '\0';
   419         }
   420         while ((spot = SDL_strchr(trimmed_name, ',')) != NULL) {
   421             SDL_memmove(spot, spot + 1, SDL_strlen(spot));
   422         }
   423 
   424         /* Initialize mapping with GUID and name */
   425         SDL_JoystickGetGUIDString(SDL_JoystickGetGUID(joystick), mapping, SDL_arraysize(mapping));
   426         SDL_strlcat(mapping, ",", SDL_arraysize(mapping));
   427         SDL_strlcat(mapping, trimmed_name, SDL_arraysize(mapping));
   428         SDL_strlcat(mapping, ",", SDL_arraysize(mapping));
   429         SDL_strlcat(mapping, "platform:", SDL_arraysize(mapping));
   430         SDL_strlcat(mapping, SDL_GetPlatform(), SDL_arraysize(mapping));
   431         SDL_strlcat(mapping, ",", SDL_arraysize(mapping));
   432 
   433         for (iIndex = 0; iIndex < SDL_arraysize(s_arrBindings); ++iIndex) {
   434             SDL_GameControllerButtonBind *pBinding = &s_arrBindings[iIndex];
   435             if (pBinding->bindType == SDL_CONTROLLER_BINDTYPE_NONE) {
   436                 continue;
   437             }
   438 
   439             if (iIndex < SDL_CONTROLLER_BUTTON_MAX) {
   440                 SDL_GameControllerButton eButton = (SDL_GameControllerButton)iIndex;
   441                 SDL_strlcat(mapping, SDL_GameControllerGetStringForButton(eButton), SDL_arraysize(mapping));
   442             } else {
   443                 SDL_GameControllerAxis eAxis = (SDL_GameControllerAxis)(iIndex - SDL_CONTROLLER_BUTTON_MAX);
   444                 SDL_strlcat(mapping, SDL_GameControllerGetStringForAxis(eAxis), SDL_arraysize(mapping));
   445             }
   446             SDL_strlcat(mapping, ":", SDL_arraysize(mapping));
   447 
   448             pszElement[0] = '\0';
   449             switch (pBinding->bindType) {
   450             case SDL_CONTROLLER_BINDTYPE_BUTTON:
   451                 SDL_snprintf(pszElement, sizeof(pszElement), "b%d", pBinding->value.button);
   452                 break;
   453             case SDL_CONTROLLER_BINDTYPE_AXIS:
   454                 SDL_snprintf(pszElement, sizeof(pszElement), "a%d", pBinding->value.axis);
   455                 break;
   456             case SDL_CONTROLLER_BINDTYPE_HAT:
   457                 SDL_snprintf(pszElement, sizeof(pszElement), "h%d.%d", pBinding->value.hat.hat, pBinding->value.hat.hat_mask);
   458                 break;
   459             default:
   460                 SDL_assert(!"Unknown bind type");
   461                 break;
   462             }
   463             SDL_strlcat(mapping, pszElement, SDL_arraysize(mapping));
   464             SDL_strlcat(mapping, ",", SDL_arraysize(mapping));
   465         }
   466 
   467         SDL_Log("Mapping:\n\n%s\n\n", mapping);
   468         /* Print to stdout as well so the user can cat the output somewhere */
   469         printf("%s\n", mapping);
   470     }
   471     
   472     SDL_DestroyRenderer(screen);
   473     SDL_DestroyWindow(window);
   474 }
   475 
   476 int
   477 main(int argc, char *argv[])
   478 {
   479     const char *name;
   480     int i;
   481     SDL_Joystick *joystick;
   482 
   483     /* Enable standard application logging */
   484     SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO);
   485 
   486     /* Initialize SDL (Note: video is required to start event loop) */
   487     if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK) < 0) {
   488         SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't initialize SDL: %s\n", SDL_GetError());
   489         exit(1);
   490     }
   491 
   492     /* Print information about the joysticks */
   493     SDL_Log("There are %d joysticks attached\n", SDL_NumJoysticks());
   494     for (i = 0; i < SDL_NumJoysticks(); ++i) {
   495         name = SDL_JoystickNameForIndex(i);
   496         SDL_Log("Joystick %d: %s\n", i, name ? name : "Unknown Joystick");
   497         joystick = SDL_JoystickOpen(i);
   498         if (joystick == NULL) {
   499             SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "SDL_JoystickOpen(%d) failed: %s\n", i,
   500                     SDL_GetError());
   501         } else {
   502             char guid[64];
   503             SDL_JoystickGetGUIDString(SDL_JoystickGetGUID(joystick),
   504                                       guid, sizeof (guid));
   505             SDL_Log("       axes: %d\n", SDL_JoystickNumAxes(joystick));
   506             SDL_Log("      balls: %d\n", SDL_JoystickNumBalls(joystick));
   507             SDL_Log("       hats: %d\n", SDL_JoystickNumHats(joystick));
   508             SDL_Log("    buttons: %d\n", SDL_JoystickNumButtons(joystick));
   509             SDL_Log("instance id: %d\n", SDL_JoystickInstanceID(joystick));
   510             SDL_Log("       guid: %s\n", guid);
   511             SDL_Log("    VID/PID: 0x%.4x/0x%.4x\n", SDL_JoystickGetVendor(joystick), SDL_JoystickGetProduct(joystick));
   512             SDL_JoystickClose(joystick);
   513         }
   514     }
   515 
   516 #ifdef __ANDROID__
   517     if (SDL_NumJoysticks() > 0) {
   518 #else
   519     if (argv[1]) {
   520 #endif
   521         int device;
   522 #ifdef __ANDROID__
   523         device = 0;
   524 #else
   525         device = atoi(argv[1]);
   526 #endif
   527         joystick = SDL_JoystickOpen(device);
   528         if (joystick == NULL) {
   529             SDL_Log("Couldn't open joystick %d: %s\n", device, SDL_GetError());
   530         } else {
   531             WatchJoystick(joystick);
   532             SDL_JoystickClose(joystick);
   533         }
   534     }
   535     else {
   536         SDL_Log("\n\nUsage: ./controllermap number\nFor example: ./controllermap 0\nOr: ./controllermap 0 >> gamecontrollerdb.txt");
   537     }
   538     SDL_QuitSubSystem(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK);
   539 
   540     return 0;
   541 }
   542 
   543 #else
   544 
   545 int
   546 main(int argc, char *argv[])
   547 {
   548     SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "SDL compiled without Joystick support.\n");
   549     exit(1);
   550 }
   551 
   552 #endif
   553 
   554 /* vi: set ts=4 sw=4 expandtab: */