| /* | 
 |   Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org> | 
 |  | 
 |   This software is provided 'as-is', without any express or implied | 
 |   warranty.  In no event will the authors be held liable for any damages | 
 |   arising from the use of this software. | 
 |  | 
 |   Permission is granted to anyone to use this software for any purpose, | 
 |   including commercial applications, and to alter it and redistribute it | 
 |   freely. | 
 | */ | 
 |  | 
 | /* Game controller mapping generator */ | 
 | /* Gabriel Jacobo <gabomdq@gmail.com> */ | 
 |  | 
 | #include <stdio.h> | 
 | #include <stdlib.h> | 
 | #include <string.h> | 
 |  | 
 | #include "SDL.h" | 
 |  | 
 | #ifndef SDL_JOYSTICK_DISABLED | 
 |  | 
 | /* Define this for verbose output while mapping controllers */ | 
 | #define DEBUG_CONTROLLERMAP | 
 |  | 
 | #ifdef __IPHONEOS__ | 
 | #define SCREEN_WIDTH    320 | 
 | #define SCREEN_HEIGHT   480 | 
 | #else | 
 | #define SCREEN_WIDTH    512 | 
 | #define SCREEN_HEIGHT   320 | 
 | #endif | 
 |  | 
 | #define MARKER_BUTTON 1 | 
 | #define MARKER_AXIS 2 | 
 |  | 
 | enum | 
 | { | 
 |     SDL_CONTROLLER_BINDING_AXIS_LEFTX_NEGATIVE, | 
 |     SDL_CONTROLLER_BINDING_AXIS_LEFTX_POSITIVE, | 
 |     SDL_CONTROLLER_BINDING_AXIS_LEFTY_NEGATIVE, | 
 |     SDL_CONTROLLER_BINDING_AXIS_LEFTY_POSITIVE, | 
 |     SDL_CONTROLLER_BINDING_AXIS_RIGHTX_NEGATIVE, | 
 |     SDL_CONTROLLER_BINDING_AXIS_RIGHTX_POSITIVE, | 
 |     SDL_CONTROLLER_BINDING_AXIS_RIGHTY_NEGATIVE, | 
 |     SDL_CONTROLLER_BINDING_AXIS_RIGHTY_POSITIVE, | 
 |     SDL_CONTROLLER_BINDING_AXIS_TRIGGERLEFT, | 
 |     SDL_CONTROLLER_BINDING_AXIS_TRIGGERRIGHT, | 
 |     SDL_CONTROLLER_BINDING_AXIS_MAX, | 
 | }; | 
 |  | 
 | #define BINDING_COUNT (SDL_CONTROLLER_BUTTON_MAX + SDL_CONTROLLER_BINDING_AXIS_MAX) | 
 |  | 
 | static struct  | 
 | { | 
 |     int x, y; | 
 |     double angle; | 
 |     int marker; | 
 |  | 
 | } s_arrBindingDisplay[BINDING_COUNT] = { | 
 |     { 387, 167, 0.0, MARKER_BUTTON }, /* SDL_CONTROLLER_BUTTON_A */ | 
 |     { 431, 132, 0.0, MARKER_BUTTON }, /* SDL_CONTROLLER_BUTTON_B */ | 
 |     { 342, 132, 0.0, MARKER_BUTTON }, /* SDL_CONTROLLER_BUTTON_X */ | 
 |     { 389, 101, 0.0, MARKER_BUTTON }, /* SDL_CONTROLLER_BUTTON_Y */ | 
 |     { 174, 132, 0.0, MARKER_BUTTON }, /* SDL_CONTROLLER_BUTTON_BACK */ | 
 |     { 233, 132, 0.0, MARKER_BUTTON }, /* SDL_CONTROLLER_BUTTON_GUIDE */ | 
 |     { 289, 132, 0.0, MARKER_BUTTON }, /* SDL_CONTROLLER_BUTTON_START */ | 
 |     {  75, 154, 0.0, MARKER_BUTTON }, /* SDL_CONTROLLER_BUTTON_LEFTSTICK */ | 
 |     { 305, 230, 0.0, MARKER_BUTTON }, /* SDL_CONTROLLER_BUTTON_RIGHTSTICK */ | 
 |     {  77,  40, 0.0, MARKER_BUTTON }, /* SDL_CONTROLLER_BUTTON_LEFTSHOULDER */ | 
 |     { 396,  36, 0.0, MARKER_BUTTON }, /* SDL_CONTROLLER_BUTTON_RIGHTSHOULDER */ | 
 |     { 154, 188, 0.0, MARKER_BUTTON }, /* SDL_CONTROLLER_BUTTON_DPAD_UP */ | 
 |     { 154, 249, 0.0, MARKER_BUTTON }, /* SDL_CONTROLLER_BUTTON_DPAD_DOWN */ | 
 |     { 116, 217, 0.0, MARKER_BUTTON }, /* SDL_CONTROLLER_BUTTON_DPAD_LEFT */ | 
 |     { 186, 217, 0.0, MARKER_BUTTON }, /* SDL_CONTROLLER_BUTTON_DPAD_RIGHT */ | 
 |     {  74, 153, 270.0, MARKER_AXIS }, /* SDL_CONTROLLER_BINDING_AXIS_LEFTX_NEGATIVE */ | 
 |     {  74, 153, 90.0,  MARKER_AXIS }, /* SDL_CONTROLLER_BINDING_AXIS_LEFTX_POSITIVE */ | 
 |     {  74, 153, 0.0,   MARKER_AXIS }, /* SDL_CONTROLLER_BINDING_AXIS_LEFTY_NEGATIVE */ | 
 |     {  74, 153, 180.0, MARKER_AXIS }, /* SDL_CONTROLLER_BINDING_AXIS_LEFTY_POSITIVE */ | 
 |     { 306, 231, 270.0, MARKER_AXIS }, /* SDL_CONTROLLER_BINDING_AXIS_RIGHTX_NEGATIVE */ | 
 |     { 306, 231, 90.0,  MARKER_AXIS }, /* SDL_CONTROLLER_BINDING_AXIS_RIGHTX_POSITIVE */ | 
 |     { 306, 231, 0.0,   MARKER_AXIS }, /* SDL_CONTROLLER_BINDING_AXIS_RIGHTY_NEGATIVE */ | 
 |     { 306, 231, 180.0, MARKER_AXIS }, /* SDL_CONTROLLER_BINDING_AXIS_RIGHTY_POSITIVE */ | 
 |     {  91, -20, 180.0, MARKER_AXIS }, /* SDL_CONTROLLER_BINDING_AXIS_TRIGGERLEFT */ | 
 |     { 375, -20, 180.0, MARKER_AXIS }, /* SDL_CONTROLLER_BINDING_AXIS_TRIGGERRIGHT */ | 
 | }; | 
 |  | 
 | static int s_arrBindingOrder[BINDING_COUNT] = { | 
 |     SDL_CONTROLLER_BUTTON_A, | 
 |     SDL_CONTROLLER_BUTTON_B, | 
 |     SDL_CONTROLLER_BUTTON_Y, | 
 |     SDL_CONTROLLER_BUTTON_X, | 
 |     SDL_CONTROLLER_BUTTON_MAX + SDL_CONTROLLER_BINDING_AXIS_LEFTX_NEGATIVE, | 
 |     SDL_CONTROLLER_BUTTON_MAX + SDL_CONTROLLER_BINDING_AXIS_LEFTX_POSITIVE, | 
 |     SDL_CONTROLLER_BUTTON_MAX + SDL_CONTROLLER_BINDING_AXIS_LEFTY_NEGATIVE, | 
 |     SDL_CONTROLLER_BUTTON_MAX + SDL_CONTROLLER_BINDING_AXIS_LEFTY_POSITIVE, | 
 |     SDL_CONTROLLER_BUTTON_LEFTSTICK, | 
 |     SDL_CONTROLLER_BUTTON_MAX + SDL_CONTROLLER_BINDING_AXIS_RIGHTX_NEGATIVE, | 
 |     SDL_CONTROLLER_BUTTON_MAX + SDL_CONTROLLER_BINDING_AXIS_RIGHTX_POSITIVE, | 
 |     SDL_CONTROLLER_BUTTON_MAX + SDL_CONTROLLER_BINDING_AXIS_RIGHTY_NEGATIVE, | 
 |     SDL_CONTROLLER_BUTTON_MAX + SDL_CONTROLLER_BINDING_AXIS_RIGHTY_POSITIVE, | 
 |     SDL_CONTROLLER_BUTTON_RIGHTSTICK, | 
 |     SDL_CONTROLLER_BUTTON_LEFTSHOULDER, | 
 |     SDL_CONTROLLER_BUTTON_MAX + SDL_CONTROLLER_BINDING_AXIS_TRIGGERLEFT, | 
 |     SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, | 
 |     SDL_CONTROLLER_BUTTON_MAX + SDL_CONTROLLER_BINDING_AXIS_TRIGGERRIGHT, | 
 |     SDL_CONTROLLER_BUTTON_DPAD_UP, | 
 |     SDL_CONTROLLER_BUTTON_DPAD_RIGHT, | 
 |     SDL_CONTROLLER_BUTTON_DPAD_DOWN, | 
 |     SDL_CONTROLLER_BUTTON_DPAD_LEFT, | 
 |     SDL_CONTROLLER_BUTTON_BACK, | 
 |     SDL_CONTROLLER_BUTTON_GUIDE, | 
 |     SDL_CONTROLLER_BUTTON_START, | 
 | }; | 
 |  | 
 | typedef struct | 
 | { | 
 |     SDL_GameControllerBindType bindType; | 
 |     union | 
 |     { | 
 |         int button; | 
 |  | 
 |         struct { | 
 |             int axis; | 
 |             int axis_min; | 
 |             int axis_max; | 
 |         } axis; | 
 |  | 
 |         struct { | 
 |             int hat; | 
 |             int hat_mask; | 
 |         } hat; | 
 |  | 
 |     } value; | 
 |  | 
 |     SDL_bool committed; | 
 |  | 
 | } SDL_GameControllerExtendedBind; | 
 |  | 
 | static SDL_GameControllerExtendedBind s_arrBindings[BINDING_COUNT]; | 
 |  | 
 | typedef struct | 
 | { | 
 |     SDL_bool m_bMoving; | 
 |     int m_nLastValue; | 
 |     int m_nStartingValue; | 
 |     int m_nFarthestValue; | 
 | } AxisState; | 
 |  | 
 | static int s_nNumAxes; | 
 | static AxisState *s_arrAxisState; | 
 |      | 
 | static int s_iCurrentBinding; | 
 | static Uint32 s_unPendingAdvanceTime; | 
 | static SDL_bool s_bBindingComplete; | 
 |  | 
 | SDL_Texture * | 
 | LoadTexture(SDL_Renderer *renderer, const char *file, SDL_bool transparent) | 
 | { | 
 |     SDL_Surface *temp; | 
 |     SDL_Texture *texture; | 
 |  | 
 |     /* Load the sprite image */ | 
 |     temp = SDL_LoadBMP(file); | 
 |     if (temp == NULL) { | 
 |         SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't load %s: %s", file, SDL_GetError()); | 
 |         return NULL; | 
 |     } | 
 |  | 
 |     /* Set transparent pixel as the pixel at (0,0) */ | 
 |     if (transparent) { | 
 |         if (temp->format->palette) { | 
 |             SDL_SetColorKey(temp, SDL_TRUE, *(Uint8 *) temp->pixels); | 
 |         } | 
 |     } | 
 |  | 
 |     /* Create textures from the image */ | 
 |     texture = SDL_CreateTextureFromSurface(renderer, temp); | 
 |     if (!texture) { | 
 |         SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create texture: %s\n", SDL_GetError()); | 
 |         SDL_FreeSurface(temp); | 
 |         return NULL; | 
 |     } | 
 |     SDL_FreeSurface(temp); | 
 |  | 
 |     /* We're ready to roll. :) */ | 
 |     return texture; | 
 | } | 
 |  | 
 | static int | 
 | StandardizeAxisValue(int nValue) | 
 | { | 
 |     if (nValue > SDL_JOYSTICK_AXIS_MAX/2) { | 
 |         return SDL_JOYSTICK_AXIS_MAX; | 
 |     } else if (nValue < SDL_JOYSTICK_AXIS_MIN/2) { | 
 |         return SDL_JOYSTICK_AXIS_MIN; | 
 |     } else { | 
 |         return 0; | 
 |     } | 
 | } | 
 |  | 
 | static void | 
 | SetCurrentBinding(int iBinding) | 
 | { | 
 |     int iIndex; | 
 |     SDL_GameControllerExtendedBind *pBinding; | 
 |  | 
 |     if (iBinding < 0) { | 
 |         return; | 
 |     } | 
 |  | 
 |     if (iBinding == BINDING_COUNT) { | 
 |         s_bBindingComplete = SDL_TRUE; | 
 |         return; | 
 |     } | 
 |  | 
 |     s_iCurrentBinding = iBinding; | 
 |  | 
 |     pBinding = &s_arrBindings[s_arrBindingOrder[s_iCurrentBinding]]; | 
 |     SDL_zerop(pBinding); | 
 |  | 
 |     for (iIndex = 0; iIndex < s_nNumAxes; ++iIndex) { | 
 |         s_arrAxisState[iIndex].m_nFarthestValue = s_arrAxisState[iIndex].m_nStartingValue; | 
 |     } | 
 |  | 
 |     s_unPendingAdvanceTime = 0; | 
 | } | 
 |  | 
 | static SDL_bool | 
 | BBindingContainsBinding(const SDL_GameControllerExtendedBind *pBindingA, const SDL_GameControllerExtendedBind *pBindingB) | 
 | { | 
 |     if (pBindingA->bindType != pBindingB->bindType) | 
 |     { | 
 |         return SDL_FALSE; | 
 |     } | 
 |     switch (pBindingA->bindType) | 
 |     { | 
 |     case SDL_CONTROLLER_BINDTYPE_AXIS: | 
 |         if (pBindingA->value.axis.axis != pBindingB->value.axis.axis) { | 
 |             return SDL_FALSE; | 
 |         } | 
 |         if (!pBindingA->committed) { | 
 |             return SDL_FALSE; | 
 |         } | 
 |         { | 
 |             int minA = SDL_min(pBindingA->value.axis.axis_min, pBindingA->value.axis.axis_max); | 
 |             int maxA = SDL_max(pBindingA->value.axis.axis_min, pBindingA->value.axis.axis_max); | 
 |             int minB = SDL_min(pBindingB->value.axis.axis_min, pBindingB->value.axis.axis_max); | 
 |             int maxB = SDL_max(pBindingB->value.axis.axis_min, pBindingB->value.axis.axis_max); | 
 |             return (minA <= minB && maxA >= maxB); | 
 |         } | 
 |         /* Not reached */ | 
 |     default: | 
 |         return SDL_memcmp(pBindingA, pBindingB, sizeof(*pBindingA)) == 0; | 
 |     } | 
 | } | 
 |  | 
 | static void | 
 | ConfigureBinding(const SDL_GameControllerExtendedBind *pBinding) | 
 | { | 
 |     SDL_GameControllerExtendedBind *pCurrent; | 
 |     int iIndex; | 
 |     int iCurrentElement = s_arrBindingOrder[s_iCurrentBinding]; | 
 |  | 
 |     /* Do we already have this binding? */ | 
 |     for (iIndex = 0; iIndex < SDL_arraysize(s_arrBindings); ++iIndex) { | 
 |         pCurrent = &s_arrBindings[iIndex]; | 
 |         if (BBindingContainsBinding(pCurrent, pBinding)) { | 
 |             if (iIndex == SDL_CONTROLLER_BUTTON_A && iCurrentElement != SDL_CONTROLLER_BUTTON_B) { | 
 |                 /* Skip to the next binding */ | 
 |                 SetCurrentBinding(s_iCurrentBinding + 1); | 
 |                 return; | 
 |             } | 
 |  | 
 |             if (iIndex == SDL_CONTROLLER_BUTTON_B) { | 
 |                 /* Go back to the previous binding */ | 
 |                 SetCurrentBinding(s_iCurrentBinding - 1); | 
 |                 return; | 
 |             } | 
 |  | 
 |             /* Already have this binding, ignore it */ | 
 |             return; | 
 |         } | 
 |     } | 
 |  | 
 | #ifdef DEBUG_CONTROLLERMAP | 
 |     switch ( pBinding->bindType ) | 
 |     { | 
 |     case SDL_CONTROLLER_BINDTYPE_NONE: | 
 |             break; | 
 |     case SDL_CONTROLLER_BINDTYPE_BUTTON: | 
 |             SDL_Log("Configuring button binding for button %d\n", pBinding->value.button); | 
 |             break; | 
 |     case SDL_CONTROLLER_BINDTYPE_AXIS: | 
 |             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"); | 
 |             break; | 
 |     case SDL_CONTROLLER_BINDTYPE_HAT: | 
 |             SDL_Log("Configuring hat binding for hat %d %d\n", pBinding->value.hat.hat, pBinding->value.hat.hat_mask); | 
 |             break; | 
 |     } | 
 | #endif /* DEBUG_CONTROLLERMAP */ | 
 |  | 
 |     /* Should the new binding override the existing one? */ | 
 |     pCurrent = &s_arrBindings[iCurrentElement]; | 
 |     if (pCurrent->bindType != SDL_CONTROLLER_BINDTYPE_NONE) { | 
 |         SDL_bool bNativeDPad, bCurrentDPad; | 
 |         SDL_bool bNativeAxis, bCurrentAxis; | 
 |          | 
 |         bNativeDPad = (iCurrentElement == SDL_CONTROLLER_BUTTON_DPAD_UP || | 
 |                        iCurrentElement == SDL_CONTROLLER_BUTTON_DPAD_DOWN || | 
 |                        iCurrentElement == SDL_CONTROLLER_BUTTON_DPAD_LEFT || | 
 |                        iCurrentElement == SDL_CONTROLLER_BUTTON_DPAD_RIGHT); | 
 |         bCurrentDPad = (pCurrent->bindType == SDL_CONTROLLER_BINDTYPE_HAT); | 
 |         if (bNativeDPad && bCurrentDPad) { | 
 |             /* We already have a binding of the type we want, ignore the new one */ | 
 |             return; | 
 |         } | 
 |  | 
 |         bNativeAxis = (iCurrentElement >= SDL_CONTROLLER_BUTTON_MAX); | 
 |         bCurrentAxis = (pCurrent->bindType == SDL_CONTROLLER_BINDTYPE_AXIS); | 
 |         if (bNativeAxis == bCurrentAxis && | 
 |             (pBinding->bindType != SDL_CONTROLLER_BINDTYPE_AXIS || | 
 |              pBinding->value.axis.axis != pCurrent->value.axis.axis)) { | 
 |             /* We already have a binding of the type we want, ignore the new one */ | 
 |             return; | 
 |         } | 
 |     } | 
 |  | 
 |     *pCurrent = *pBinding; | 
 |  | 
 |     if (pBinding->committed) { | 
 |         s_unPendingAdvanceTime = SDL_GetTicks(); | 
 |     } else { | 
 |         s_unPendingAdvanceTime = 0; | 
 |     } | 
 | } | 
 |  | 
 | static SDL_bool | 
 | BMergeAxisBindings(int iIndex) | 
 | { | 
 |     SDL_GameControllerExtendedBind *pBindingA = &s_arrBindings[iIndex]; | 
 |     SDL_GameControllerExtendedBind *pBindingB = &s_arrBindings[iIndex+1]; | 
 |     if (pBindingA->bindType == SDL_CONTROLLER_BINDTYPE_AXIS && | 
 |         pBindingB->bindType == SDL_CONTROLLER_BINDTYPE_AXIS && | 
 |         pBindingA->value.axis.axis == pBindingB->value.axis.axis) { | 
 |         if (pBindingA->value.axis.axis_min == pBindingB->value.axis.axis_min) { | 
 |             pBindingA->value.axis.axis_min = pBindingA->value.axis.axis_max; | 
 |             pBindingA->value.axis.axis_max = pBindingB->value.axis.axis_max; | 
 |             pBindingB->bindType = SDL_CONTROLLER_BINDTYPE_NONE; | 
 |             return SDL_TRUE; | 
 |         } | 
 |     } | 
 |     return SDL_FALSE; | 
 | } | 
 |  | 
 | static void | 
 | WatchJoystick(SDL_Joystick * joystick) | 
 | { | 
 |     SDL_Window *window = NULL; | 
 |     SDL_Renderer *screen = NULL; | 
 |     SDL_Texture *background, *button, *axis, *marker; | 
 |     const char *name = NULL; | 
 |     SDL_bool done = SDL_FALSE; | 
 |     SDL_Event event; | 
 |     SDL_Rect dst; | 
 |     Uint8 alpha=200, alpha_step = -1; | 
 |     Uint32 alpha_ticks = 0; | 
 |     SDL_JoystickID nJoystickID; | 
 |     int iIndex; | 
 |  | 
 |     /* Create a window to display joystick axis position */ | 
 |     window = SDL_CreateWindow("Game Controller Map", SDL_WINDOWPOS_CENTERED, | 
 |                               SDL_WINDOWPOS_CENTERED, SCREEN_WIDTH, | 
 |                               SCREEN_HEIGHT, 0); | 
 |     if (window == NULL) { | 
 |         SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create window: %s\n", SDL_GetError()); | 
 |         return; | 
 |     } | 
 |  | 
 |     screen = SDL_CreateRenderer(window, -1, 0); | 
 |     if (screen == NULL) { | 
 |         SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create renderer: %s\n", SDL_GetError()); | 
 |         SDL_DestroyWindow(window); | 
 |         return; | 
 |     } | 
 |      | 
 |     background = LoadTexture(screen, "controllermap.bmp", SDL_FALSE); | 
 |     button = LoadTexture(screen, "button.bmp", SDL_TRUE); | 
 |     axis = LoadTexture(screen, "axis.bmp", SDL_TRUE); | 
 |     SDL_RaiseWindow(window); | 
 |  | 
 |     /* scale for platforms that don't give you the window size you asked for. */ | 
 |     SDL_RenderSetLogicalSize(screen, SCREEN_WIDTH, SCREEN_HEIGHT); | 
 |  | 
 |     /* Print info about the joystick we are watching */ | 
 |     name = SDL_JoystickName(joystick); | 
 |     SDL_Log("Watching joystick %d: (%s)\n", SDL_JoystickInstanceID(joystick), | 
 |            name ? name : "Unknown Joystick"); | 
 |     SDL_Log("Joystick has %d axes, %d hats, %d balls, and %d buttons\n", | 
 |            SDL_JoystickNumAxes(joystick), SDL_JoystickNumHats(joystick), | 
 |            SDL_JoystickNumBalls(joystick), SDL_JoystickNumButtons(joystick)); | 
 |      | 
 |     SDL_Log("\n\n\ | 
 |     ====================================================================================\n\ | 
 |     Press the buttons on your controller when indicated\n\ | 
 |     (Your controller may look different than the picture)\n\ | 
 |     If you want to correct a mistake, press backspace or the back button on your device\n\ | 
 |     To skip a button, press SPACE or click/touch the screen\n\ | 
 |     To exit, press ESC\n\ | 
 |     ====================================================================================\n"); | 
 |  | 
 |     nJoystickID = SDL_JoystickInstanceID(joystick); | 
 |  | 
 |     s_nNumAxes = SDL_JoystickNumAxes(joystick); | 
 |     s_arrAxisState = (AxisState *)SDL_calloc(s_nNumAxes, sizeof(*s_arrAxisState)); | 
 |  | 
 | 	/* Skip any spurious events at start */ | 
 | 	while (SDL_PollEvent(&event) > 0) { | 
 | 		continue; | 
 | 	} | 
 |  | 
 |     /* Loop, getting joystick events! */ | 
 |     while (!done && !s_bBindingComplete) { | 
 |         int iElement = s_arrBindingOrder[s_iCurrentBinding]; | 
 |  | 
 |         switch (s_arrBindingDisplay[iElement].marker) { | 
 |             case MARKER_AXIS: | 
 |                 marker = axis; | 
 |                 break; | 
 |             case MARKER_BUTTON: | 
 |                 marker = button; | 
 |                 break; | 
 |             default: | 
 |                 break; | 
 |         } | 
 |          | 
 |         dst.x = s_arrBindingDisplay[iElement].x; | 
 |         dst.y = s_arrBindingDisplay[iElement].y; | 
 |         SDL_QueryTexture(marker, NULL, NULL, &dst.w, &dst.h); | 
 |  | 
 |         if (SDL_GetTicks() - alpha_ticks > 5) { | 
 |             alpha_ticks = SDL_GetTicks(); | 
 |             alpha += alpha_step; | 
 |             if (alpha == 255) { | 
 |                 alpha_step = -1; | 
 |             } | 
 |             if (alpha < 128) { | 
 |                 alpha_step = 1; | 
 |             } | 
 |         } | 
 |  | 
 |         SDL_SetRenderDrawColor(screen, 0xFF, 0xFF, 0xFF, SDL_ALPHA_OPAQUE); | 
 |         SDL_RenderClear(screen); | 
 |         SDL_RenderCopy(screen, background, NULL, NULL); | 
 |         SDL_SetTextureAlphaMod(marker, alpha); | 
 |         SDL_SetTextureColorMod(marker, 10, 255, 21); | 
 |         SDL_RenderCopyEx(screen, marker, NULL, &dst, s_arrBindingDisplay[iElement].angle, NULL, SDL_FLIP_NONE); | 
 |         SDL_RenderPresent(screen); | 
 |              | 
 |         while (SDL_PollEvent(&event) > 0) { | 
 |             switch (event.type) { | 
 |             case SDL_JOYDEVICEREMOVED: | 
 |                 if (event.jaxis.which == nJoystickID) { | 
 |                     done = SDL_TRUE; | 
 |                 } | 
 |                 break; | 
 |             case SDL_JOYAXISMOTION: | 
 |                 if (event.jaxis.which == nJoystickID) { | 
 |                     const int MAX_ALLOWED_JITTER = SDL_JOYSTICK_AXIS_MAX / 80;  /* ShanWan PS3 controller needed 96 */ | 
 |                     AxisState *pAxisState = &s_arrAxisState[event.jaxis.axis]; | 
 |                     int nValue = event.jaxis.value; | 
 |                     int nCurrentDistance, nFarthestDistance; | 
 |                     if (!pAxisState->m_bMoving) { | 
 |                         Sint16 nInitialValue; | 
 |                         pAxisState->m_bMoving = SDL_JoystickGetAxisInitialState(joystick, event.jaxis.axis, &nInitialValue); | 
 |                         pAxisState->m_nLastValue = nInitialValue; | 
 |                         pAxisState->m_nStartingValue = nInitialValue; | 
 |                         pAxisState->m_nFarthestValue = nInitialValue; | 
 |                     } else if (SDL_abs(nValue - pAxisState->m_nLastValue) <= MAX_ALLOWED_JITTER) { | 
 |                         break; | 
 |                     } else { | 
 |                         pAxisState->m_nLastValue = nValue; | 
 |                     } | 
 |                     nCurrentDistance = SDL_abs(nValue - pAxisState->m_nStartingValue); | 
 |                     nFarthestDistance = SDL_abs(pAxisState->m_nFarthestValue - pAxisState->m_nStartingValue); | 
 |                     if (nCurrentDistance > nFarthestDistance) { | 
 |                         pAxisState->m_nFarthestValue = nValue; | 
 |                         nFarthestDistance = SDL_abs(pAxisState->m_nFarthestValue - pAxisState->m_nStartingValue); | 
 |                     } | 
 |  | 
 | #ifdef DEBUG_CONTROLLERMAP | 
 |                     SDL_Log("AXIS %d nValue %d nCurrentDistance %d nFarthestDistance %d\n", event.jaxis.axis, nValue, nCurrentDistance, nFarthestDistance); | 
 | #endif | 
 |                     if (nFarthestDistance >= 16000) { | 
 |                         /* If we've gone out far enough and started to come back, let's bind this axis */ | 
 |                         SDL_bool bCommitBinding = (nCurrentDistance <= 10000) ? SDL_TRUE : SDL_FALSE; | 
 |                         SDL_GameControllerExtendedBind binding; | 
 |                         SDL_zero(binding); | 
 |                         binding.bindType = SDL_CONTROLLER_BINDTYPE_AXIS; | 
 |                         binding.value.axis.axis = event.jaxis.axis; | 
 |                         binding.value.axis.axis_min = StandardizeAxisValue(pAxisState->m_nStartingValue); | 
 |                         binding.value.axis.axis_max = StandardizeAxisValue(pAxisState->m_nFarthestValue); | 
 |                         binding.committed = bCommitBinding; | 
 |                         ConfigureBinding(&binding); | 
 |                     } | 
 |                 } | 
 |                 break; | 
 |             case SDL_JOYHATMOTION: | 
 |                 if (event.jhat.which == nJoystickID) { | 
 |                     if (event.jhat.value != SDL_HAT_CENTERED) { | 
 |                         SDL_GameControllerExtendedBind binding; | 
 |  | 
 | #ifdef DEBUG_CONTROLLERMAP | 
 |                         SDL_Log("HAT %d %d\n", event.jhat.hat, event.jhat.value); | 
 | #endif | 
 |                         SDL_zero(binding); | 
 |                         binding.bindType = SDL_CONTROLLER_BINDTYPE_HAT; | 
 |                         binding.value.hat.hat = event.jhat.hat; | 
 |                         binding.value.hat.hat_mask = event.jhat.value; | 
 |                         binding.committed = SDL_TRUE; | 
 |                         ConfigureBinding(&binding); | 
 |                     } | 
 |                 } | 
 |                 break; | 
 |             case SDL_JOYBALLMOTION: | 
 |                 break; | 
 |             case SDL_JOYBUTTONDOWN: | 
 |                 if (event.jbutton.which == nJoystickID) { | 
 |                     SDL_GameControllerExtendedBind binding; | 
 |  | 
 | #ifdef DEBUG_CONTROLLERMAP | 
 |                     SDL_Log("BUTTON %d\n", event.jbutton.button); | 
 | #endif | 
 |                     SDL_zero(binding); | 
 |                     binding.bindType = SDL_CONTROLLER_BINDTYPE_BUTTON; | 
 |                     binding.value.button = event.jbutton.button; | 
 |                     binding.committed = SDL_TRUE; | 
 |                     ConfigureBinding(&binding); | 
 |                 } | 
 |                 break; | 
 |             case SDL_FINGERDOWN: | 
 |             case SDL_MOUSEBUTTONDOWN: | 
 |                 /* Skip this step */ | 
 |                 SetCurrentBinding(s_iCurrentBinding + 1); | 
 |                 break; | 
 |             case SDL_KEYDOWN: | 
 |                 if (event.key.keysym.sym == SDLK_BACKSPACE || event.key.keysym.sym == SDLK_AC_BACK) { | 
 |                     SetCurrentBinding(s_iCurrentBinding - 1); | 
 |                     break; | 
 |                 } | 
 |                 if (event.key.keysym.sym == SDLK_SPACE) { | 
 |                     SetCurrentBinding(s_iCurrentBinding + 1); | 
 |                     break; | 
 |                 } | 
 |  | 
 |                 if ((event.key.keysym.sym != SDLK_ESCAPE)) { | 
 |                     break; | 
 |                 } | 
 |                 /* Fall through to signal quit */ | 
 |             case SDL_QUIT: | 
 |                 done = SDL_TRUE; | 
 |                 break; | 
 |             default: | 
 |                 break; | 
 |             } | 
 |         } | 
 |  | 
 |         SDL_Delay(15); | 
 |  | 
 |         /* Wait 100 ms for joystick events to stop coming in, | 
 |            in case a controller sends multiple events for a single control (e.g. axis and button for trigger) | 
 |         */ | 
 |         if (s_unPendingAdvanceTime && SDL_GetTicks() - s_unPendingAdvanceTime >= 100) { | 
 |             SetCurrentBinding(s_iCurrentBinding + 1); | 
 |         } | 
 |     } | 
 |  | 
 |     if (s_bBindingComplete) { | 
 |         char mapping[1024]; | 
 |         char trimmed_name[128]; | 
 |         char *spot; | 
 |         int iIndex; | 
 |         char pszElement[12]; | 
 |  | 
 |         SDL_strlcpy(trimmed_name, name, SDL_arraysize(trimmed_name)); | 
 |         while (SDL_isspace(trimmed_name[0])) { | 
 |             SDL_memmove(&trimmed_name[0], &trimmed_name[1], SDL_strlen(trimmed_name)); | 
 |         } | 
 |         while (trimmed_name[0] && SDL_isspace(trimmed_name[SDL_strlen(trimmed_name) - 1])) { | 
 |             trimmed_name[SDL_strlen(trimmed_name) - 1] = '\0'; | 
 |         } | 
 |         while ((spot = SDL_strchr(trimmed_name, ',')) != NULL) { | 
 |             SDL_memmove(spot, spot + 1, SDL_strlen(spot)); | 
 |         } | 
 |  | 
 |         /* Initialize mapping with GUID and name */ | 
 |         SDL_JoystickGetGUIDString(SDL_JoystickGetGUID(joystick), mapping, SDL_arraysize(mapping)); | 
 |         SDL_strlcat(mapping, ",", SDL_arraysize(mapping)); | 
 |         SDL_strlcat(mapping, trimmed_name, SDL_arraysize(mapping)); | 
 |         SDL_strlcat(mapping, ",", SDL_arraysize(mapping)); | 
 |         SDL_strlcat(mapping, "platform:", SDL_arraysize(mapping)); | 
 |         SDL_strlcat(mapping, SDL_GetPlatform(), SDL_arraysize(mapping)); | 
 |         SDL_strlcat(mapping, ",", SDL_arraysize(mapping)); | 
 |  | 
 |         for (iIndex = 0; iIndex < SDL_arraysize(s_arrBindings); ++iIndex) { | 
 |             SDL_GameControllerExtendedBind *pBinding = &s_arrBindings[iIndex]; | 
 |             if (pBinding->bindType == SDL_CONTROLLER_BINDTYPE_NONE) { | 
 |                 continue; | 
 |             } | 
 |  | 
 |             if (iIndex < SDL_CONTROLLER_BUTTON_MAX) { | 
 |                 SDL_GameControllerButton eButton = (SDL_GameControllerButton)iIndex; | 
 |                 SDL_strlcat(mapping, SDL_GameControllerGetStringForButton(eButton), SDL_arraysize(mapping)); | 
 |             } else { | 
 |                 const char *pszAxisName; | 
 |                 switch (iIndex - SDL_CONTROLLER_BUTTON_MAX) { | 
 |                 case SDL_CONTROLLER_BINDING_AXIS_LEFTX_NEGATIVE: | 
 |                     if (!BMergeAxisBindings(iIndex)) { | 
 |                         SDL_strlcat(mapping, "-", SDL_arraysize(mapping)); | 
 |                     } | 
 |                     pszAxisName = SDL_GameControllerGetStringForAxis(SDL_CONTROLLER_AXIS_LEFTX); | 
 |                     break; | 
 |                 case SDL_CONTROLLER_BINDING_AXIS_LEFTX_POSITIVE: | 
 |                     SDL_strlcat(mapping, "+", SDL_arraysize(mapping)); | 
 |                     pszAxisName = SDL_GameControllerGetStringForAxis(SDL_CONTROLLER_AXIS_LEFTX); | 
 |                     break; | 
 |                 case SDL_CONTROLLER_BINDING_AXIS_LEFTY_NEGATIVE: | 
 |                     if (!BMergeAxisBindings(iIndex)) { | 
 |                         SDL_strlcat(mapping, "-", SDL_arraysize(mapping)); | 
 |                     } | 
 |                     pszAxisName = SDL_GameControllerGetStringForAxis(SDL_CONTROLLER_AXIS_LEFTY); | 
 |                     break; | 
 |                 case SDL_CONTROLLER_BINDING_AXIS_LEFTY_POSITIVE: | 
 |                     SDL_strlcat(mapping, "+", SDL_arraysize(mapping)); | 
 |                     pszAxisName = SDL_GameControllerGetStringForAxis(SDL_CONTROLLER_AXIS_LEFTY); | 
 |                     break; | 
 |                 case SDL_CONTROLLER_BINDING_AXIS_RIGHTX_NEGATIVE: | 
 |                     if (!BMergeAxisBindings(iIndex)) { | 
 |                         SDL_strlcat(mapping, "-", SDL_arraysize(mapping)); | 
 |                     } | 
 |                     pszAxisName = SDL_GameControllerGetStringForAxis(SDL_CONTROLLER_AXIS_RIGHTX); | 
 |                     break; | 
 |                 case SDL_CONTROLLER_BINDING_AXIS_RIGHTX_POSITIVE: | 
 |                     SDL_strlcat(mapping, "+", SDL_arraysize(mapping)); | 
 |                     pszAxisName = SDL_GameControllerGetStringForAxis(SDL_CONTROLLER_AXIS_RIGHTX); | 
 |                     break; | 
 |                 case SDL_CONTROLLER_BINDING_AXIS_RIGHTY_NEGATIVE: | 
 |                     if (!BMergeAxisBindings(iIndex)) { | 
 |                         SDL_strlcat(mapping, "-", SDL_arraysize(mapping)); | 
 |                     } | 
 |                     pszAxisName = SDL_GameControllerGetStringForAxis(SDL_CONTROLLER_AXIS_RIGHTY); | 
 |                     break; | 
 |                 case SDL_CONTROLLER_BINDING_AXIS_RIGHTY_POSITIVE: | 
 |                     SDL_strlcat(mapping, "+", SDL_arraysize(mapping)); | 
 |                     pszAxisName = SDL_GameControllerGetStringForAxis(SDL_CONTROLLER_AXIS_RIGHTY); | 
 |                     break; | 
 |                 case SDL_CONTROLLER_BINDING_AXIS_TRIGGERLEFT: | 
 |                     pszAxisName = SDL_GameControllerGetStringForAxis(SDL_CONTROLLER_AXIS_TRIGGERLEFT); | 
 |                     break; | 
 |                 case SDL_CONTROLLER_BINDING_AXIS_TRIGGERRIGHT: | 
 |                     pszAxisName = SDL_GameControllerGetStringForAxis(SDL_CONTROLLER_AXIS_TRIGGERRIGHT); | 
 |                     break; | 
 |                 } | 
 |                 SDL_strlcat(mapping, pszAxisName, SDL_arraysize(mapping)); | 
 |             } | 
 |             SDL_strlcat(mapping, ":", SDL_arraysize(mapping)); | 
 |  | 
 |             pszElement[0] = '\0'; | 
 |             switch (pBinding->bindType) { | 
 |             case SDL_CONTROLLER_BINDTYPE_BUTTON: | 
 |                 SDL_snprintf(pszElement, sizeof(pszElement), "b%d", pBinding->value.button); | 
 |                 break; | 
 |             case SDL_CONTROLLER_BINDTYPE_AXIS: | 
 |                 if (pBinding->value.axis.axis_min == 0 && pBinding->value.axis.axis_max == SDL_JOYSTICK_AXIS_MIN) { | 
 |                     /* The negative half axis */ | 
 |                     SDL_snprintf(pszElement, sizeof(pszElement), "-a%d", pBinding->value.axis.axis); | 
 |                 } else if (pBinding->value.axis.axis_min == 0 && pBinding->value.axis.axis_max == SDL_JOYSTICK_AXIS_MAX) { | 
 |                     /* The positive half axis */ | 
 |                     SDL_snprintf(pszElement, sizeof(pszElement), "+a%d", pBinding->value.axis.axis); | 
 |                 } else { | 
 |                     SDL_snprintf(pszElement, sizeof(pszElement), "a%d", pBinding->value.axis.axis); | 
 |                     if (pBinding->value.axis.axis_min > pBinding->value.axis.axis_max) { | 
 |                         /* Invert the axis */ | 
 |                         SDL_strlcat(pszElement, "~", SDL_arraysize(pszElement)); | 
 |                     } | 
 |                 } | 
 |                 break; | 
 |             case SDL_CONTROLLER_BINDTYPE_HAT: | 
 |                 SDL_snprintf(pszElement, sizeof(pszElement), "h%d.%d", pBinding->value.hat.hat, pBinding->value.hat.hat_mask); | 
 |                 break; | 
 |             default: | 
 |                 SDL_assert(!"Unknown bind type"); | 
 |                 break; | 
 |             } | 
 |             SDL_strlcat(mapping, pszElement, SDL_arraysize(mapping)); | 
 |             SDL_strlcat(mapping, ",", SDL_arraysize(mapping)); | 
 |         } | 
 |  | 
 |         SDL_Log("Mapping:\n\n%s\n\n", mapping); | 
 |         /* Print to stdout as well so the user can cat the output somewhere */ | 
 |         printf("%s\n", mapping); | 
 |     } | 
 |  | 
 |     SDL_free(s_arrAxisState); | 
 |     s_arrAxisState = NULL; | 
 |      | 
 |     SDL_DestroyRenderer(screen); | 
 |     SDL_DestroyWindow(window); | 
 | } | 
 |  | 
 | int | 
 | main(int argc, char *argv[]) | 
 | { | 
 |     const char *name; | 
 |     int i; | 
 |     SDL_Joystick *joystick; | 
 |  | 
 |     /* Enable standard application logging */ | 
 |     SDL_LogSetPriority(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_INFO); | 
 |  | 
 |     /* Initialize SDL (Note: video is required to start event loop) */ | 
 |     if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK) < 0) { | 
 |         SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't initialize SDL: %s\n", SDL_GetError()); | 
 |         exit(1); | 
 |     } | 
 |  | 
 |     /* Print information about the joysticks */ | 
 |     SDL_Log("There are %d joysticks attached\n", SDL_NumJoysticks()); | 
 |     for (i = 0; i < SDL_NumJoysticks(); ++i) { | 
 |         name = SDL_JoystickNameForIndex(i); | 
 |         SDL_Log("Joystick %d: %s\n", i, name ? name : "Unknown Joystick"); | 
 |         joystick = SDL_JoystickOpen(i); | 
 |         if (joystick == NULL) { | 
 |             SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "SDL_JoystickOpen(%d) failed: %s\n", i, | 
 |                     SDL_GetError()); | 
 |         } else { | 
 |             char guid[64]; | 
 |             SDL_JoystickGetGUIDString(SDL_JoystickGetGUID(joystick), | 
 |                                       guid, sizeof (guid)); | 
 |             SDL_Log("       axes: %d\n", SDL_JoystickNumAxes(joystick)); | 
 |             SDL_Log("      balls: %d\n", SDL_JoystickNumBalls(joystick)); | 
 |             SDL_Log("       hats: %d\n", SDL_JoystickNumHats(joystick)); | 
 |             SDL_Log("    buttons: %d\n", SDL_JoystickNumButtons(joystick)); | 
 |             SDL_Log("instance id: %d\n", SDL_JoystickInstanceID(joystick)); | 
 |             SDL_Log("       guid: %s\n", guid); | 
 |             SDL_Log("    VID/PID: 0x%.4x/0x%.4x\n", SDL_JoystickGetVendor(joystick), SDL_JoystickGetProduct(joystick)); | 
 |             SDL_JoystickClose(joystick); | 
 |         } | 
 |     } | 
 |  | 
 | #ifdef __ANDROID__ | 
 |     if (SDL_NumJoysticks() > 0) { | 
 | #else | 
 |     if (argv[1]) { | 
 | #endif | 
 |         int device; | 
 | #ifdef __ANDROID__ | 
 |         device = 0; | 
 | #else | 
 |         device = atoi(argv[1]); | 
 | #endif | 
 |         joystick = SDL_JoystickOpen(device); | 
 |         if (joystick == NULL) { | 
 |             SDL_Log("Couldn't open joystick %d: %s\n", device, SDL_GetError()); | 
 |         } else { | 
 |             WatchJoystick(joystick); | 
 |             SDL_JoystickClose(joystick); | 
 |         } | 
 |     } | 
 |     else { | 
 |         SDL_Log("\n\nUsage: ./controllermap number\nFor example: ./controllermap 0\nOr: ./controllermap 0 >> gamecontrollerdb.txt"); | 
 |     } | 
 |     SDL_QuitSubSystem(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK); | 
 |  | 
 |     return 0; | 
 | } | 
 |  | 
 | #else | 
 |  | 
 | int | 
 | main(int argc, char *argv[]) | 
 | { | 
 |     SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "SDL compiled without Joystick support.\n"); | 
 |     return 1; | 
 | } | 
 |  | 
 | #endif | 
 |  | 
 | /* vi: set ts=4 sw=4 expandtab: */ |