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