test/controllermap.c
author Sam Lantinga <slouken@libsdl.org>
Thu, 12 Oct 2017 14:25:07 -0700
changeset 11615 f5b657276413
parent 10930 badc1f1411ef
child 11811 5d94cb6b24d3
permissions -rw-r--r--
Fixed bug 3877 - missing SDLCALL in SDLTest_ExampleHitTestCallback

Ozkan Sezer

Following trivial patch adds missing SDLCALL to SDLTest_ExampleHitTestCallback()
     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: */