test/controllermap.c
author Philipp Wiesemann <philipp.wiesemann@arcor.de>
Fri, 16 Dec 2016 22:58:32 +0100
changeset 10707 28c1a3f9c88b
parent 10706 71ce5d7d2610
child 10724 5ea5f198879f
permissions -rw-r--r--
Fixed hotplug with more than one device in testjoystick program.
     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     SDL_JoystickID nJoystickID;
   237     Uint32 unDeflectedAxes = 0;
   238 
   239     /* Create a window to display joystick axis position */
   240     window = SDL_CreateWindow("Game Controller Map", SDL_WINDOWPOS_CENTERED,
   241                               SDL_WINDOWPOS_CENTERED, SCREEN_WIDTH,
   242                               SCREEN_HEIGHT, 0);
   243     if (window == NULL) {
   244         SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create window: %s\n", SDL_GetError());
   245         return;
   246     }
   247 
   248     screen = SDL_CreateRenderer(window, -1, 0);
   249     if (screen == NULL) {
   250         SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create renderer: %s\n", SDL_GetError());
   251         SDL_DestroyWindow(window);
   252         return;
   253     }
   254     
   255     background = LoadTexture(screen, "controllermap.bmp", SDL_FALSE);
   256     button = LoadTexture(screen, "button.bmp", SDL_TRUE);
   257     axis = LoadTexture(screen, "axis.bmp", SDL_TRUE);
   258     SDL_RaiseWindow(window);
   259 
   260     /* scale for platforms that don't give you the window size you asked for. */
   261     SDL_RenderSetLogicalSize(screen, SCREEN_WIDTH, SCREEN_HEIGHT);
   262 
   263     /* Print info about the joystick we are watching */
   264     name = SDL_JoystickName(joystick);
   265     SDL_Log("Watching joystick %d: (%s)\n", SDL_JoystickInstanceID(joystick),
   266            name ? name : "Unknown Joystick");
   267     SDL_Log("Joystick has %d axes, %d hats, %d balls, and %d buttons\n",
   268            SDL_JoystickNumAxes(joystick), SDL_JoystickNumHats(joystick),
   269            SDL_JoystickNumBalls(joystick), SDL_JoystickNumButtons(joystick));
   270     
   271     SDL_Log("\n\n\
   272     ====================================================================================\n\
   273     Press the buttons on your controller when indicated\n\
   274     (Your controller may look different than the picture)\n\
   275     If you want to correct a mistake, press backspace or the back button on your device\n\
   276     To skip a button, press SPACE or click/touch the screen\n\
   277     To exit, press ESC\n\
   278     ====================================================================================\n");
   279 
   280     nJoystickID = SDL_JoystickInstanceID(joystick);
   281 
   282     /* Loop, getting joystick events! */
   283     while (!done && !s_bBindingComplete) {
   284         int iElement = s_arrBindingOrder[s_iCurrentBinding];
   285 
   286         switch (s_arrBindingDisplay[iElement].marker) {
   287             case MARKER_AXIS:
   288                 marker = axis;
   289                 break;
   290             case MARKER_BUTTON:
   291                 marker = button;
   292                 break;
   293             default:
   294                 break;
   295         }
   296         
   297         dst.x = s_arrBindingDisplay[iElement].x;
   298         dst.y = s_arrBindingDisplay[iElement].y;
   299         SDL_QueryTexture(marker, NULL, NULL, &dst.w, &dst.h);
   300 
   301         if (SDL_GetTicks() - alpha_ticks > 5) {
   302             alpha_ticks = SDL_GetTicks();
   303             alpha += alpha_step;
   304             if (alpha == 255) {
   305                 alpha_step = -1;
   306             }
   307             if (alpha < 128) {
   308                 alpha_step = 1;
   309             }
   310         }
   311 
   312         SDL_SetRenderDrawColor(screen, 0xFF, 0xFF, 0xFF, SDL_ALPHA_OPAQUE);
   313         SDL_RenderClear(screen);
   314         SDL_RenderCopy(screen, background, NULL, NULL);
   315         SDL_SetTextureAlphaMod(marker, alpha);
   316         SDL_SetTextureColorMod(marker, 10, 255, 21);
   317         SDL_RenderCopyEx(screen, marker, NULL, &dst, s_arrBindingDisplay[iElement].angle, NULL, SDL_FLIP_NONE);
   318         SDL_RenderPresent(screen);
   319             
   320         while (SDL_PollEvent(&event) > 0) {
   321             switch (event.type) {
   322             case SDL_JOYDEVICEREMOVED:
   323                 if (event.jaxis.which == nJoystickID) {
   324                     done = SDL_TRUE;
   325                 }
   326                 break;
   327             case SDL_JOYAXISMOTION:
   328                 if (event.jaxis.which == nJoystickID) {
   329                     uint32_t unAxisMask = (1 << event.jaxis.axis);
   330                     SDL_bool bDeflected = (event.jaxis.value <= -20000 || event.jaxis.value >= 20000);
   331                     if (bDeflected && !(unDeflectedAxes & unAxisMask)) {
   332                         SDL_GameControllerButtonBind binding;
   333                         SDL_zero(binding);
   334                         binding.bindType = SDL_CONTROLLER_BINDTYPE_AXIS;
   335                         binding.value.axis = event.jaxis.axis;
   336                         ConfigureBinding(&binding);
   337                     }
   338                     if (bDeflected) {
   339                         unDeflectedAxes |= unAxisMask;
   340                     } else {
   341                         unDeflectedAxes &= ~unAxisMask;
   342                     }
   343                 }
   344                 break;
   345             case SDL_JOYHATMOTION:
   346                 if (event.jhat.which == nJoystickID) {
   347                     if (event.jhat.value != SDL_HAT_CENTERED) {
   348                         SDL_GameControllerButtonBind binding;
   349                         SDL_zero(binding);
   350                         binding.bindType = SDL_CONTROLLER_BINDTYPE_HAT;
   351                         binding.value.hat.hat = event.jhat.hat;
   352                         binding.value.hat.hat_mask = event.jhat.value;
   353                         ConfigureBinding(&binding);
   354                     }
   355                 }
   356                 break;
   357             case SDL_JOYBALLMOTION:
   358                 break;
   359             case SDL_JOYBUTTONDOWN:
   360                 if (event.jbutton.which == nJoystickID) {
   361                     SDL_GameControllerButtonBind binding;
   362                     SDL_zero(binding);
   363                     binding.bindType = SDL_CONTROLLER_BINDTYPE_BUTTON;
   364                     binding.value.button = event.jbutton.button;
   365                     ConfigureBinding(&binding);
   366                 }
   367                 break;
   368             case SDL_FINGERDOWN:
   369             case SDL_MOUSEBUTTONDOWN:
   370                 /* Skip this step */
   371                 SetCurrentBinding(s_iCurrentBinding + 1);
   372                 break;
   373             case SDL_KEYDOWN:
   374                 if (event.key.keysym.sym == SDLK_BACKSPACE || event.key.keysym.sym == SDLK_AC_BACK) {
   375                     SetCurrentBinding(s_iCurrentBinding - 1);
   376                     break;
   377                 }
   378                 if (event.key.keysym.sym == SDLK_SPACE) {
   379                     SetCurrentBinding(s_iCurrentBinding + 1);
   380                     break;
   381                 }
   382 
   383                 if ((event.key.keysym.sym != SDLK_ESCAPE)) {
   384                     break;
   385                 }
   386                 /* Fall through to signal quit */
   387             case SDL_QUIT:
   388                 done = SDL_TRUE;
   389                 break;
   390             default:
   391                 break;
   392             }
   393         }
   394 
   395         SDL_Delay(15);
   396 
   397         /* Wait 100 ms for joystick events to stop coming in,
   398            in case a controller sends multiple events for a single control (e.g. axis and button for trigger)
   399         */
   400         if (s_unPendingAdvanceTime && SDL_GetTicks() - s_unPendingAdvanceTime >= 100) {
   401             SetCurrentBinding(s_iCurrentBinding + 1);
   402         }
   403     }
   404 
   405     if (s_bBindingComplete) {
   406         char mapping[1024];
   407         char trimmed_name[128];
   408         char *spot;
   409         int iIndex;
   410         char pszElement[12];
   411 
   412         SDL_strlcpy(trimmed_name, name, SDL_arraysize(trimmed_name));
   413         while (SDL_isspace(trimmed_name[0])) {
   414             SDL_memmove(&trimmed_name[0], &trimmed_name[1], SDL_strlen(trimmed_name));
   415         }
   416         while (trimmed_name[0] && SDL_isspace(trimmed_name[SDL_strlen(trimmed_name) - 1])) {
   417             trimmed_name[SDL_strlen(trimmed_name) - 1] = '\0';
   418         }
   419         while ((spot = SDL_strchr(trimmed_name, ',')) != NULL) {
   420             SDL_memmove(spot, spot + 1, SDL_strlen(spot));
   421         }
   422 
   423         /* Initialize mapping with GUID and name */
   424         SDL_JoystickGetGUIDString(SDL_JoystickGetGUID(joystick), mapping, SDL_arraysize(mapping));
   425         SDL_strlcat(mapping, ",", SDL_arraysize(mapping));
   426         SDL_strlcat(mapping, trimmed_name, SDL_arraysize(mapping));
   427         SDL_strlcat(mapping, ",", SDL_arraysize(mapping));
   428         SDL_strlcat(mapping, "platform:", SDL_arraysize(mapping));
   429         SDL_strlcat(mapping, SDL_GetPlatform(), SDL_arraysize(mapping));
   430         SDL_strlcat(mapping, ",", SDL_arraysize(mapping));
   431 
   432         for (iIndex = 0; iIndex < SDL_arraysize(s_arrBindings); ++iIndex) {
   433             SDL_GameControllerButtonBind *pBinding = &s_arrBindings[iIndex];
   434             if (pBinding->bindType == SDL_CONTROLLER_BINDTYPE_NONE) {
   435                 continue;
   436             }
   437 
   438             if (iIndex < SDL_CONTROLLER_BUTTON_MAX) {
   439                 SDL_GameControllerButton eButton = (SDL_GameControllerButton)iIndex;
   440                 SDL_strlcat(mapping, SDL_GameControllerGetStringForButton(eButton), SDL_arraysize(mapping));
   441             } else {
   442                 SDL_GameControllerAxis eAxis = (SDL_GameControllerAxis)(iIndex - SDL_CONTROLLER_BUTTON_MAX);
   443                 SDL_strlcat(mapping, SDL_GameControllerGetStringForAxis(eAxis), SDL_arraysize(mapping));
   444             }
   445             SDL_strlcat(mapping, ":", SDL_arraysize(mapping));
   446 
   447             pszElement[0] = '\0';
   448             switch (pBinding->bindType) {
   449             case SDL_CONTROLLER_BINDTYPE_BUTTON:
   450                 SDL_snprintf(pszElement, sizeof(pszElement), "b%d", pBinding->value.button);
   451                 break;
   452             case SDL_CONTROLLER_BINDTYPE_AXIS:
   453                 SDL_snprintf(pszElement, sizeof(pszElement), "a%d", pBinding->value.axis);
   454                 break;
   455             case SDL_CONTROLLER_BINDTYPE_HAT:
   456                 SDL_snprintf(pszElement, sizeof(pszElement), "h%d.%d", pBinding->value.hat.hat, pBinding->value.hat.hat_mask);
   457                 break;
   458             default:
   459                 SDL_assert(!"Unknown bind type");
   460                 break;
   461             }
   462             SDL_strlcat(mapping, pszElement, SDL_arraysize(mapping));
   463             SDL_strlcat(mapping, ",", SDL_arraysize(mapping));
   464         }
   465 
   466         SDL_Log("Mapping:\n\n%s\n\n", mapping);
   467         /* Print to stdout as well so the user can cat the output somewhere */
   468         printf("%s\n", mapping);
   469     }
   470     
   471     SDL_DestroyRenderer(screen);
   472     SDL_DestroyWindow(window);
   473 }
   474 
   475 int
   476 main(int argc, char *argv[])
   477 {
   478     const char *name;
   479     int i;
   480     SDL_Joystick *joystick;
   481 
   482     /* Enable standard application logging */
   483     SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO);
   484 
   485     /* Initialize SDL (Note: video is required to start event loop) */
   486     if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK) < 0) {
   487         SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't initialize SDL: %s\n", SDL_GetError());
   488         exit(1);
   489     }
   490 
   491     /* Print information about the joysticks */
   492     SDL_Log("There are %d joysticks attached\n", SDL_NumJoysticks());
   493     for (i = 0; i < SDL_NumJoysticks(); ++i) {
   494         name = SDL_JoystickNameForIndex(i);
   495         SDL_Log("Joystick %d: %s\n", i, name ? name : "Unknown Joystick");
   496         joystick = SDL_JoystickOpen(i);
   497         if (joystick == NULL) {
   498             SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "SDL_JoystickOpen(%d) failed: %s\n", i,
   499                     SDL_GetError());
   500         } else {
   501             char guid[64];
   502             SDL_JoystickGetGUIDString(SDL_JoystickGetGUID(joystick),
   503                                       guid, sizeof (guid));
   504             SDL_Log("       axes: %d\n", SDL_JoystickNumAxes(joystick));
   505             SDL_Log("      balls: %d\n", SDL_JoystickNumBalls(joystick));
   506             SDL_Log("       hats: %d\n", SDL_JoystickNumHats(joystick));
   507             SDL_Log("    buttons: %d\n", SDL_JoystickNumButtons(joystick));
   508             SDL_Log("instance id: %d\n", SDL_JoystickInstanceID(joystick));
   509             SDL_Log("       guid: %s\n", guid);
   510             SDL_Log("    VID/PID: 0x%.4x/0x%.4x\n", SDL_JoystickGetVendor(joystick), SDL_JoystickGetProduct(joystick));
   511             SDL_JoystickClose(joystick);
   512         }
   513     }
   514 
   515 #ifdef __ANDROID__
   516     if (SDL_NumJoysticks() > 0) {
   517 #else
   518     if (argv[1]) {
   519 #endif
   520         int device;
   521 #ifdef __ANDROID__
   522         device = 0;
   523 #else
   524         device = atoi(argv[1]);
   525 #endif
   526         joystick = SDL_JoystickOpen(device);
   527         if (joystick == NULL) {
   528             SDL_Log("Couldn't open joystick %d: %s\n", device, SDL_GetError());
   529         } else {
   530             WatchJoystick(joystick);
   531             SDL_JoystickClose(joystick);
   532         }
   533     }
   534     else {
   535         SDL_Log("\n\nUsage: ./controllermap number\nFor example: ./controllermap 0\nOr: ./controllermap 0 >> gamecontrollerdb.txt");
   536     }
   537     SDL_QuitSubSystem(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK);
   538 
   539     return 0;
   540 }
   541 
   542 #else
   543 
   544 int
   545 main(int argc, char *argv[])
   546 {
   547     SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "SDL compiled without Joystick support.\n");
   548     exit(1);
   549 }
   550 
   551 #endif
   552 
   553 /* vi: set ts=4 sw=4 expandtab: */