src/joystick/SDL_gamecontroller.c
author David Ludwig <dludwig@pobox.com>
Tue, 24 Dec 2013 21:28:31 -0500
changeset 8552 abd934eac415
parent 8543 b9dd3cf38585
child 8582 c3e9a2b93517
permissions -rw-r--r--
WinRT: moved ill-performing XInput device-detection calls to a separate thread
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2013 Sam Lantinga <slouken@libsdl.org>
     4 
     5   This software is provided 'as-is', without any express or implied
     6   warranty.  In no event will the authors be held liable for any damages
     7   arising from the use of this software.
     8 
     9   Permission is granted to anyone to use this software for any purpose,
    10   including commercial applications, and to alter it and redistribute it
    11   freely, subject to the following restrictions:
    12 
    13   1. The origin of this software must not be misrepresented; you must not
    14      claim that you wrote the original software. If you use this software
    15      in a product, an acknowledgment in the product documentation would be
    16      appreciated but is not required.
    17   2. Altered source versions must be plainly marked as such, and must not be
    18      misrepresented as being the original software.
    19   3. This notice may not be removed or altered from any source distribution.
    20 */
    21 #include "SDL_config.h"
    22 
    23 /* This is the game controller API for Simple DirectMedia Layer */
    24 
    25 #include "SDL_events.h"
    26 #include "SDL_assert.h"
    27 #include "SDL_sysjoystick.h"
    28 #include "SDL_hints.h"
    29 #include "SDL_gamecontrollerdb.h"
    30 
    31 #if !SDL_EVENTS_DISABLED
    32 #include "../events/SDL_events_c.h"
    33 #endif
    34 #define ABS(_x) ((_x) < 0 ? -(_x) : (_x))
    35 
    36 
    37 /* a list of currently opened game controllers */
    38 static SDL_GameController *SDL_gamecontrollers = NULL;
    39 
    40 /* keep track of the hat and mask value that transforms this hat movement into a button/axis press */
    41 struct _SDL_HatMapping
    42 {
    43     int hat;
    44     Uint8 mask;
    45 };
    46 
    47 #define k_nMaxReverseEntries 20
    48 
    49 /**
    50  * We are encoding the "HAT" as 0xhm. where h == hat ID and m == mask
    51  * MAX 4 hats supported
    52  */
    53 #define k_nMaxHatEntries 0x3f + 1
    54 
    55 /* our in memory mapping db between joystick objects and controller mappings */
    56 struct _SDL_ControllerMapping
    57 {
    58     SDL_JoystickGUID guid;
    59     const char *name;
    60 
    61     /* mapping of axis/button id to controller version */
    62     int axes[SDL_CONTROLLER_AXIS_MAX];
    63     int buttonasaxis[SDL_CONTROLLER_AXIS_MAX];
    64 
    65     int buttons[SDL_CONTROLLER_BUTTON_MAX];
    66     int axesasbutton[SDL_CONTROLLER_BUTTON_MAX];
    67     struct _SDL_HatMapping hatasbutton[SDL_CONTROLLER_BUTTON_MAX];
    68 
    69     /* reverse mapping, joystick indices to buttons */
    70     SDL_GameControllerAxis raxes[k_nMaxReverseEntries];
    71     SDL_GameControllerAxis rbuttonasaxis[k_nMaxReverseEntries];
    72 
    73     SDL_GameControllerButton rbuttons[k_nMaxReverseEntries];
    74     SDL_GameControllerButton raxesasbutton[k_nMaxReverseEntries];
    75     SDL_GameControllerButton rhatasbutton[k_nMaxHatEntries];
    76 
    77 };
    78 
    79 
    80 /* our hard coded list of mapping support */
    81 typedef struct _ControllerMapping_t
    82 {
    83     SDL_JoystickGUID guid;
    84     char *name;
    85     char *mapping;
    86     struct _ControllerMapping_t *next;
    87 } ControllerMapping_t;
    88 
    89 static ControllerMapping_t *s_pSupportedControllers = NULL;
    90 #if defined(SDL_JOYSTICK_DINPUT) || defined(SDL_JOYSTICK_XINPUT)
    91 static ControllerMapping_t *s_pXInputMapping = NULL;
    92 #endif
    93 
    94 /* The SDL game controller structure */
    95 struct _SDL_GameController
    96 {
    97     SDL_Joystick *joystick; /* underlying joystick device */
    98     int ref_count;
    99     Uint8 hatState[4]; /* the current hat state for this controller */
   100     struct _SDL_ControllerMapping mapping; /* the mapping object for this controller */
   101     struct _SDL_GameController *next; /* pointer to next game controller we have allocated */
   102 };
   103 
   104 
   105 int SDL_PrivateGameControllerAxis(SDL_GameController * gamecontroller, SDL_GameControllerAxis axis, Sint16 value);
   106 int SDL_PrivateGameControllerButton(SDL_GameController * gamecontroller, SDL_GameControllerButton button, Uint8 state);
   107 
   108 /*
   109  * Event filter to fire controller events from joystick ones
   110  */
   111 int SDL_GameControllerEventWatcher(void *userdata, SDL_Event * event)
   112 {
   113     switch( event->type )
   114     {
   115     case SDL_JOYAXISMOTION:
   116         {
   117             SDL_GameController *controllerlist;
   118 
   119             if ( event->jaxis.axis >= k_nMaxReverseEntries ) break;
   120 
   121             controllerlist = SDL_gamecontrollers;
   122             while ( controllerlist )
   123             {
   124                 if ( controllerlist->joystick->instance_id == event->jaxis.which )
   125                 {
   126                     if ( controllerlist->mapping.raxes[event->jaxis.axis] >= 0 ) /* simple axis to axis, send it through */
   127                     {
   128                         SDL_GameControllerAxis axis = controllerlist->mapping.raxes[event->jaxis.axis];
   129                         Sint16 value = event->jaxis.value;
   130                         switch (axis)
   131                         {
   132                             case SDL_CONTROLLER_AXIS_TRIGGERLEFT:
   133                             case SDL_CONTROLLER_AXIS_TRIGGERRIGHT:
   134                                 /* Shift it to be 0 - 32767. */
   135                                 value = value / 2 + 16384;
   136                             default:
   137                                 break;
   138                         }
   139                         SDL_PrivateGameControllerAxis( controllerlist, axis, value );
   140                     }
   141                     else if ( controllerlist->mapping.raxesasbutton[event->jaxis.axis] >= 0 ) /* simulate an axis as a button */
   142                     {
   143                         SDL_PrivateGameControllerButton( controllerlist, controllerlist->mapping.raxesasbutton[event->jaxis.axis], ABS(event->jaxis.value) > 32768/2 ? SDL_PRESSED : SDL_RELEASED );
   144                     }
   145                     break;
   146                 }
   147                 controllerlist = controllerlist->next;
   148             }
   149         }
   150         break;
   151     case SDL_JOYBUTTONDOWN:
   152     case SDL_JOYBUTTONUP:
   153         {
   154             SDL_GameController *controllerlist;
   155 
   156             if ( event->jbutton.button >= k_nMaxReverseEntries ) break;
   157 
   158             controllerlist = SDL_gamecontrollers;
   159             while ( controllerlist )
   160             {
   161                 if ( controllerlist->joystick->instance_id == event->jbutton.which )
   162                 {
   163                     if ( controllerlist->mapping.rbuttons[event->jbutton.button] >= 0 ) /* simple button as button */
   164                     {
   165                         SDL_PrivateGameControllerButton( controllerlist, controllerlist->mapping.rbuttons[event->jbutton.button], event->jbutton.state );
   166                     }
   167                     else if ( controllerlist->mapping.rbuttonasaxis[event->jbutton.button] >= 0 ) /* an button pretending to be an axis */
   168                     {
   169                         SDL_PrivateGameControllerAxis( controllerlist, controllerlist->mapping.rbuttonasaxis[event->jbutton.button], event->jbutton.state > 0 ? 32767 : 0 );
   170                     }
   171                     break;
   172                 }
   173                 controllerlist = controllerlist->next;
   174             }
   175         }
   176         break;
   177     case SDL_JOYHATMOTION:
   178         {
   179             SDL_GameController *controllerlist;
   180 
   181             if ( event->jhat.hat >= 4 ) break;
   182 
   183             controllerlist = SDL_gamecontrollers;
   184             while ( controllerlist )
   185             {
   186                 if ( controllerlist->joystick->instance_id == event->jhat.which )
   187                 {
   188                     Uint8 bSame = controllerlist->hatState[event->jhat.hat] & event->jhat.value;
   189                     /* Get list of removed bits (button release) */
   190                     Uint8 bChanged = controllerlist->hatState[event->jhat.hat] ^ bSame;
   191                     /* the hat idx in the high nibble */
   192                     int bHighHat = event->jhat.hat << 4;
   193 
   194                     if ( bChanged & SDL_HAT_DOWN )
   195                         SDL_PrivateGameControllerButton( controllerlist, controllerlist->mapping.rhatasbutton[bHighHat | SDL_HAT_DOWN], SDL_RELEASED );
   196                     if ( bChanged & SDL_HAT_UP )
   197                         SDL_PrivateGameControllerButton( controllerlist, controllerlist->mapping.rhatasbutton[bHighHat | SDL_HAT_UP], SDL_RELEASED );
   198                     if ( bChanged & SDL_HAT_LEFT )
   199                         SDL_PrivateGameControllerButton( controllerlist, controllerlist->mapping.rhatasbutton[bHighHat | SDL_HAT_LEFT], SDL_RELEASED );
   200                     if ( bChanged & SDL_HAT_RIGHT )
   201                         SDL_PrivateGameControllerButton( controllerlist, controllerlist->mapping.rhatasbutton[bHighHat | SDL_HAT_RIGHT], SDL_RELEASED );
   202 
   203                     /* Get list of added bits (button press) */
   204                     bChanged = event->jhat.value ^ bSame;
   205 
   206                     if ( bChanged & SDL_HAT_DOWN )
   207                         SDL_PrivateGameControllerButton( controllerlist, controllerlist->mapping.rhatasbutton[bHighHat | SDL_HAT_DOWN], SDL_PRESSED );
   208                     if ( bChanged & SDL_HAT_UP )
   209                         SDL_PrivateGameControllerButton( controllerlist, controllerlist->mapping.rhatasbutton[bHighHat | SDL_HAT_UP], SDL_PRESSED );
   210                     if ( bChanged & SDL_HAT_LEFT )
   211                         SDL_PrivateGameControllerButton( controllerlist, controllerlist->mapping.rhatasbutton[bHighHat | SDL_HAT_LEFT], SDL_PRESSED );
   212                     if ( bChanged & SDL_HAT_RIGHT )
   213                         SDL_PrivateGameControllerButton( controllerlist, controllerlist->mapping.rhatasbutton[bHighHat | SDL_HAT_RIGHT], SDL_PRESSED );
   214 
   215                     /* update our state cache */
   216                     controllerlist->hatState[event->jhat.hat] = event->jhat.value;
   217 
   218                     break;
   219                 }
   220                 controllerlist = controllerlist->next;
   221             }
   222         }
   223         break;
   224     case SDL_JOYDEVICEADDED:
   225         {
   226             if ( SDL_IsGameController(event->jdevice.which ) )
   227             {
   228                 SDL_Event deviceevent;
   229                 deviceevent.type = SDL_CONTROLLERDEVICEADDED;
   230                 deviceevent.cdevice.which = event->jdevice.which;
   231                 SDL_PushEvent(&deviceevent);
   232             }
   233         }
   234         break;
   235     case SDL_JOYDEVICEREMOVED:
   236         {
   237             SDL_GameController *controllerlist = SDL_gamecontrollers;
   238             while ( controllerlist )
   239             {
   240                 if ( controllerlist->joystick->instance_id == event->jdevice.which )
   241                 {
   242                     SDL_Event deviceevent;
   243                     deviceevent.type = SDL_CONTROLLERDEVICEREMOVED;
   244                     deviceevent.cdevice.which = event->jdevice.which;
   245                     SDL_PushEvent(&deviceevent);
   246                     break;
   247                 }
   248                 controllerlist = controllerlist->next;
   249             }
   250         }
   251         break;
   252     default:
   253         break;
   254     }
   255 
   256     return 1;
   257 }
   258 
   259 /*
   260  * Helper function to scan the mappings database for a controller with the specified GUID
   261  */
   262 ControllerMapping_t *SDL_PrivateGetControllerMappingForGUID(SDL_JoystickGUID *guid)
   263 {
   264     ControllerMapping_t *pSupportedController = s_pSupportedControllers;
   265     while ( pSupportedController )
   266     {
   267         if ( !SDL_memcmp( guid, &pSupportedController->guid, sizeof(*guid) ) )
   268         {
   269             return pSupportedController;
   270         }
   271         pSupportedController = pSupportedController->next;
   272     }
   273     return NULL;
   274 }
   275 
   276 /*
   277  * Helper function to determine pre-calculated offset to certain joystick mappings
   278  */
   279 ControllerMapping_t *SDL_PrivateGetControllerMapping(int device_index)
   280 {
   281 #if defined(SDL_JOYSTICK_DINPUT) || defined(SDL_JOYSTICK_XINPUT)
   282     if ( SDL_SYS_IsXInputDeviceIndex(device_index) && s_pXInputMapping )
   283     {
   284         return s_pXInputMapping;
   285     }
   286     else
   287 #endif
   288     {
   289         SDL_JoystickGUID jGUID = SDL_JoystickGetDeviceGUID( device_index );
   290         return SDL_PrivateGetControllerMappingForGUID(&jGUID);
   291     }
   292 }
   293 
   294 static const char* map_StringForControllerAxis[] = {
   295     "leftx",
   296     "lefty",
   297     "rightx",
   298     "righty",
   299     "lefttrigger",
   300     "righttrigger",
   301     NULL
   302 };
   303 
   304 /*
   305  * convert a string to its enum equivalent
   306  */
   307 SDL_GameControllerAxis SDL_GameControllerGetAxisFromString( const char *pchString )
   308 {
   309     int entry;
   310     if ( !pchString || !pchString[0] )
   311         return SDL_CONTROLLER_AXIS_INVALID;
   312 
   313     for ( entry = 0; map_StringForControllerAxis[entry]; ++entry)
   314     {
   315         if ( !SDL_strcasecmp( pchString, map_StringForControllerAxis[entry] ) )
   316             return entry;
   317     }
   318     return SDL_CONTROLLER_AXIS_INVALID;
   319 }
   320 
   321 /*
   322  * convert an enum to its string equivalent
   323  */
   324 const char* SDL_GameControllerGetStringForAxis( SDL_GameControllerAxis axis )
   325 {
   326     if (axis > SDL_CONTROLLER_AXIS_INVALID && axis < SDL_CONTROLLER_AXIS_MAX)
   327     {
   328         return map_StringForControllerAxis[axis];
   329     }
   330     return NULL;
   331 }
   332 
   333 static const char* map_StringForControllerButton[] = {
   334     "a",
   335     "b",
   336     "x",
   337     "y",
   338     "back",
   339     "guide",
   340     "start",
   341     "leftstick",
   342     "rightstick",
   343     "leftshoulder",
   344     "rightshoulder",
   345     "dpup",
   346     "dpdown",
   347     "dpleft",
   348     "dpright",
   349     NULL
   350 };
   351 
   352 /*
   353  * convert a string to its enum equivalent
   354  */
   355 SDL_GameControllerButton SDL_GameControllerGetButtonFromString( const char *pchString )
   356 {
   357     int entry;
   358     if ( !pchString || !pchString[0] )
   359         return SDL_CONTROLLER_BUTTON_INVALID;
   360 
   361     for ( entry = 0; map_StringForControllerButton[entry]; ++entry)
   362     {
   363         if ( !SDL_strcasecmp( pchString, map_StringForControllerButton[entry] ) )
   364         return entry;
   365     }
   366     return SDL_CONTROLLER_BUTTON_INVALID;
   367 }
   368 
   369 /*
   370  * convert an enum to its string equivalent
   371  */
   372 const char* SDL_GameControllerGetStringForButton( SDL_GameControllerButton axis )
   373 {
   374     if (axis > SDL_CONTROLLER_BUTTON_INVALID && axis < SDL_CONTROLLER_BUTTON_MAX)
   375     {
   376         return map_StringForControllerButton[axis];
   377     }
   378     return NULL;
   379 }
   380 
   381 /*
   382  * given a controller button name and a joystick name update our mapping structure with it
   383  */
   384 void SDL_PrivateGameControllerParseButton( const char *szGameButton, const char *szJoystickButton, struct _SDL_ControllerMapping *pMapping )
   385 {
   386     int iSDLButton = 0;
   387     SDL_GameControllerButton button;
   388     SDL_GameControllerAxis axis;
   389     button = SDL_GameControllerGetButtonFromString( szGameButton );
   390     axis = SDL_GameControllerGetAxisFromString( szGameButton );
   391     iSDLButton = SDL_atoi( &szJoystickButton[1] );
   392 
   393     if ( szJoystickButton[0] == 'a' )
   394     {
   395         if ( iSDLButton >= k_nMaxReverseEntries )
   396         {
   397             SDL_SetError("Axis index too large: %d", iSDLButton );
   398             return;
   399         }
   400         if ( axis != SDL_CONTROLLER_AXIS_INVALID )
   401         {
   402             pMapping->axes[ axis ] = iSDLButton;
   403             pMapping->raxes[ iSDLButton ] = axis;
   404         }
   405         else if ( button != SDL_CONTROLLER_BUTTON_INVALID )
   406         {
   407             pMapping->axesasbutton[ button ] = iSDLButton;
   408             pMapping->raxesasbutton[ iSDLButton ] = button;
   409         }
   410         else
   411         {
   412             SDL_assert( !"How did we get here?" );
   413         }
   414 
   415     }
   416     else if ( szJoystickButton[0] == 'b' )
   417     {
   418         if ( iSDLButton >= k_nMaxReverseEntries )
   419         {
   420             SDL_SetError("Button index too large: %d", iSDLButton );
   421             return;
   422         }
   423         if ( button != SDL_CONTROLLER_BUTTON_INVALID )
   424         {
   425             pMapping->buttons[ button ] = iSDLButton;
   426             pMapping->rbuttons[ iSDLButton ] = button;
   427         }
   428         else if ( axis != SDL_CONTROLLER_AXIS_INVALID )
   429         {
   430             pMapping->buttonasaxis[ axis ] = iSDLButton;
   431             pMapping->rbuttonasaxis[ iSDLButton ] = axis;
   432         }
   433         else
   434         {
   435             SDL_assert( !"How did we get here?" );
   436         }
   437     }
   438     else if ( szJoystickButton[0] == 'h' )
   439     {
   440         int hat = SDL_atoi( &szJoystickButton[1] );
   441         int mask = SDL_atoi( &szJoystickButton[3] );
   442         if (hat >= 4) {
   443             SDL_SetError("Hat index too large: %d", iSDLButton );
   444         }
   445 
   446         if ( button != SDL_CONTROLLER_BUTTON_INVALID )
   447         {
   448             int ridx;
   449             pMapping->hatasbutton[ button ].hat = hat;
   450             pMapping->hatasbutton[ button ].mask = mask;
   451             ridx = (hat << 4) | mask;
   452             pMapping->rhatasbutton[ ridx ] = button;
   453         }
   454         else if ( axis != SDL_CONTROLLER_AXIS_INVALID )
   455         {
   456             SDL_assert( !"Support hat as axis" );
   457         }
   458         else
   459         {
   460             SDL_assert( !"How did we get here?" );
   461         }
   462     }
   463 }
   464 
   465 
   466 /*
   467  * given a controller mapping string update our mapping object
   468  */
   469 static void
   470 SDL_PrivateGameControllerParseControllerConfigString( struct _SDL_ControllerMapping *pMapping, const char *pchString )
   471 {
   472     char szGameButton[20];
   473     char szJoystickButton[20];
   474     SDL_bool bGameButton = SDL_TRUE;
   475     int i = 0;
   476     const char *pchPos = pchString;
   477 
   478     SDL_memset( szGameButton, 0x0, sizeof(szGameButton) );
   479     SDL_memset( szJoystickButton, 0x0, sizeof(szJoystickButton) );
   480 
   481     while ( pchPos && *pchPos )
   482     {
   483         if ( *pchPos == ':' )
   484         {
   485             i = 0;
   486             bGameButton = SDL_FALSE;
   487         }
   488         else if ( *pchPos == ' ' )
   489         {
   490 
   491         }
   492         else if ( *pchPos == ',' )
   493         {
   494             i = 0;
   495             bGameButton = SDL_TRUE;
   496             SDL_PrivateGameControllerParseButton( szGameButton, szJoystickButton, pMapping );
   497             SDL_memset( szGameButton, 0x0, sizeof(szGameButton) );
   498             SDL_memset( szJoystickButton, 0x0, sizeof(szJoystickButton) );
   499 
   500         }
   501         else if ( bGameButton )
   502         {
   503             if ( i >=  sizeof(szGameButton))
   504             {
   505                 SDL_SetError( "Button name too large: %s", szGameButton );
   506                 return;
   507             }
   508             szGameButton[i] = *pchPos;
   509             i++;
   510         }
   511         else
   512         {
   513             if ( i >=  sizeof(szJoystickButton))
   514             {
   515                 SDL_SetError( "Joystick button name too large: %s", szJoystickButton );
   516                 return;
   517             }
   518             szJoystickButton[i] = *pchPos;
   519             i++;
   520         }
   521         pchPos++;
   522     }
   523 
   524     SDL_PrivateGameControllerParseButton( szGameButton, szJoystickButton, pMapping );
   525 
   526 }
   527 
   528 /*
   529  * Make a new button mapping struct
   530  */
   531 void SDL_PrivateLoadButtonMapping( struct _SDL_ControllerMapping *pMapping, SDL_JoystickGUID guid, const char *pchName, const char *pchMapping )
   532 {
   533     int j;
   534 
   535     pMapping->guid = guid;
   536     pMapping->name = pchName;
   537 
   538     /* set all the button mappings to non defaults */
   539     for ( j = 0; j < SDL_CONTROLLER_AXIS_MAX; j++ )
   540     {
   541         pMapping->axes[j] = -1;
   542         pMapping->buttonasaxis[j] = -1;
   543     }
   544     for ( j = 0; j < SDL_CONTROLLER_BUTTON_MAX; j++ )
   545     {
   546         pMapping->buttons[j] = -1;
   547         pMapping->axesasbutton[j] = -1;
   548         pMapping->hatasbutton[j].hat = -1;
   549     }
   550 
   551     for ( j = 0; j < k_nMaxReverseEntries; j++ )
   552     {
   553         pMapping->raxes[j] = SDL_CONTROLLER_AXIS_INVALID;
   554         pMapping->rbuttonasaxis[j] = SDL_CONTROLLER_AXIS_INVALID;
   555         pMapping->rbuttons[j] = SDL_CONTROLLER_BUTTON_INVALID;
   556         pMapping->raxesasbutton[j] = SDL_CONTROLLER_BUTTON_INVALID;
   557     }
   558 
   559     for (j = 0; j < k_nMaxHatEntries; j++)
   560     {
   561         pMapping->rhatasbutton[j] = SDL_CONTROLLER_BUTTON_INVALID;
   562     }
   563 
   564     SDL_PrivateGameControllerParseControllerConfigString( pMapping, pchMapping );
   565 }
   566 
   567 
   568 /*
   569  * grab the guid string from a mapping string
   570  */
   571 char *SDL_PrivateGetControllerGUIDFromMappingString( const char *pMapping )
   572 {
   573     const char *pFirstComma = SDL_strchr( pMapping, ',' );
   574     if ( pFirstComma )
   575     {
   576         char *pchGUID = SDL_malloc( pFirstComma - pMapping + 1 );
   577         if ( !pchGUID )
   578         {
   579             SDL_OutOfMemory();
   580             return NULL;
   581         }
   582         SDL_memcpy( pchGUID, pMapping, pFirstComma - pMapping );
   583         pchGUID[ pFirstComma - pMapping ] = 0;
   584         return pchGUID;
   585     }
   586     return NULL;
   587 }
   588 
   589 
   590 /*
   591  * grab the name string from a mapping string
   592  */
   593 char *SDL_PrivateGetControllerNameFromMappingString( const char *pMapping )
   594 {
   595     const char *pFirstComma, *pSecondComma;
   596     char *pchName;
   597 
   598     pFirstComma = SDL_strchr( pMapping, ',' );
   599     if ( !pFirstComma )
   600         return NULL;
   601 
   602     pSecondComma = SDL_strchr( pFirstComma + 1, ',' );
   603     if ( !pSecondComma )
   604         return NULL;
   605 
   606     pchName = SDL_malloc( pSecondComma - pFirstComma );
   607     if ( !pchName )
   608     {
   609         SDL_OutOfMemory();
   610         return NULL;
   611     }
   612     SDL_memcpy( pchName, pFirstComma + 1, pSecondComma - pFirstComma );
   613     pchName[ pSecondComma - pFirstComma - 1 ] = 0;
   614     return pchName;
   615 }
   616 
   617 
   618 /*
   619  * grab the button mapping string from a mapping string
   620  */
   621 char *SDL_PrivateGetControllerMappingFromMappingString( const char *pMapping )
   622 {
   623     const char *pFirstComma, *pSecondComma;
   624 
   625     pFirstComma = SDL_strchr( pMapping, ',' );
   626     if ( !pFirstComma )
   627         return NULL;
   628 
   629     pSecondComma = SDL_strchr( pFirstComma + 1, ',' );
   630     if ( !pSecondComma )
   631         return NULL;
   632 
   633     return SDL_strdup(pSecondComma + 1); /* mapping is everything after the 3rd comma */
   634 }
   635 
   636 void SDL_PrivateGameControllerRefreshMapping( ControllerMapping_t *pControllerMapping )
   637 {
   638     SDL_GameController *gamecontrollerlist = SDL_gamecontrollers;
   639     while ( gamecontrollerlist )
   640     {
   641         if ( !SDL_memcmp( &gamecontrollerlist->mapping.guid, &pControllerMapping->guid, sizeof(pControllerMapping->guid) ) )
   642         {
   643             SDL_Event event;
   644             event.type = SDL_CONTROLLERDEVICEREMAPPED;
   645             event.cdevice.which = gamecontrollerlist->joystick->instance_id;
   646             SDL_PushEvent(&event);
   647 
   648             /* Not really threadsafe.  Should this lock access within SDL_GameControllerEventWatcher? */
   649             SDL_PrivateLoadButtonMapping(&gamecontrollerlist->mapping, pControllerMapping->guid, pControllerMapping->name, pControllerMapping->mapping);
   650         }
   651 
   652         gamecontrollerlist = gamecontrollerlist->next;
   653     }
   654 }
   655 
   656 /*
   657  * Add or update an entry into the Mappings Database
   658  */
   659 int
   660 SDL_GameControllerAddMapping( const char *mappingString )
   661 {
   662     char *pchGUID;
   663     char *pchName;
   664     char *pchMapping;
   665     SDL_JoystickGUID jGUID;
   666     ControllerMapping_t *pControllerMapping;
   667 #if defined(SDL_JOYSTICK_DINPUT) || defined(SDL_JOYSTICK_XINPUT)
   668     SDL_bool is_xinput_mapping = SDL_FALSE;
   669 #endif
   670 
   671     pchGUID = SDL_PrivateGetControllerGUIDFromMappingString( mappingString );
   672     if (!pchGUID) {
   673         return -1;
   674     }
   675 #if defined(SDL_JOYSTICK_DINPUT) || defined(SDL_JOYSTICK_XINPUT)
   676     if ( !SDL_strcasecmp( pchGUID, "xinput" ) ) {
   677         is_xinput_mapping = SDL_TRUE;
   678     }
   679 #endif
   680     jGUID = SDL_JoystickGetGUIDFromString(pchGUID);
   681     SDL_free(pchGUID);
   682 
   683     pControllerMapping = SDL_PrivateGetControllerMappingForGUID(&jGUID);
   684 
   685     pchName = SDL_PrivateGetControllerNameFromMappingString( mappingString );
   686     if (!pchName) return -1;
   687 
   688     pchMapping = SDL_PrivateGetControllerMappingFromMappingString( mappingString );
   689     if (!pchMapping) {
   690         SDL_free( pchName );
   691         return -1;
   692     }
   693 
   694     if (pControllerMapping) {
   695         /* Update existing mapping */
   696         SDL_free( pControllerMapping->name );
   697         pControllerMapping->name = pchName;
   698         SDL_free( pControllerMapping->mapping );
   699         pControllerMapping->mapping = pchMapping;
   700         /* refresh open controllers */
   701         SDL_PrivateGameControllerRefreshMapping( pControllerMapping );
   702         return 0;
   703     } else {
   704         pControllerMapping = SDL_malloc( sizeof(*pControllerMapping) );
   705         if (!pControllerMapping) {
   706             SDL_free( pchName );
   707             SDL_free( pchMapping );
   708             return SDL_OutOfMemory();
   709         }
   710 #if defined(SDL_JOYSTICK_DINPUT) || defined(SDL_JOYSTICK_XINPUT)
   711         if ( is_xinput_mapping )
   712         {
   713             s_pXInputMapping = pControllerMapping;
   714         }
   715 #endif
   716         pControllerMapping->guid = jGUID;
   717         pControllerMapping->name = pchName;
   718         pControllerMapping->mapping = pchMapping;
   719         pControllerMapping->next = s_pSupportedControllers;
   720         s_pSupportedControllers = pControllerMapping;
   721         return 1;
   722     }
   723 }
   724 
   725 /*
   726  * Get the mapping string for this GUID
   727  */
   728 char *
   729 SDL_GameControllerMappingForGUID( SDL_JoystickGUID guid )
   730 {
   731     char *pMappingString = NULL;
   732     ControllerMapping_t *mapping = SDL_PrivateGetControllerMappingForGUID(&guid);
   733     if (mapping) {
   734         char pchGUID[33];
   735         size_t needed;
   736         SDL_JoystickGetGUIDString(guid, pchGUID, sizeof(pchGUID));
   737         /* allocate enough memory for GUID + ',' + name + ',' + mapping + \0 */
   738         needed = SDL_strlen(pchGUID) + 1 + SDL_strlen(mapping->name) + 1 + SDL_strlen(mapping->mapping) + 1;
   739         pMappingString = SDL_malloc( needed );
   740         SDL_snprintf( pMappingString, needed, "%s,%s,%s", pchGUID, mapping->name, mapping->mapping );
   741     }
   742     return pMappingString;
   743 }
   744 
   745 /*
   746  * Get the mapping string for this device
   747  */
   748 char *
   749 SDL_GameControllerMapping( SDL_GameController * gamecontroller )
   750 {
   751     return SDL_GameControllerMappingForGUID( gamecontroller->mapping.guid );
   752 }
   753 
   754 static void
   755 SDL_GameControllerLoadHints()
   756 {
   757     const char *hint = SDL_GetHint(SDL_HINT_GAMECONTROLLERCONFIG);
   758     if ( hint && hint[0] ) {
   759         size_t nchHints = SDL_strlen( hint );
   760         char *pUserMappings = SDL_malloc( nchHints + 1 );
   761         char *pTempMappings = pUserMappings;
   762         SDL_memcpy( pUserMappings, hint, nchHints );
   763         while ( pUserMappings ) {
   764             char *pchNewLine = NULL;
   765 
   766             pchNewLine = SDL_strchr( pUserMappings, '\n' );
   767             if ( pchNewLine )
   768                 *pchNewLine = '\0';
   769 
   770             SDL_GameControllerAddMapping( pUserMappings );
   771 
   772             if ( pchNewLine )
   773                 pUserMappings = pchNewLine + 1;
   774             else
   775                 pUserMappings = NULL;
   776         }
   777         SDL_free(pTempMappings);
   778     }
   779 }
   780 
   781 /*
   782  * Initialize the game controller system, mostly load our DB of controller config mappings
   783  */
   784 int
   785 SDL_GameControllerInit(void)
   786 {
   787     int i = 0;
   788     const char *pMappingString = NULL;
   789     s_pSupportedControllers = NULL;
   790     pMappingString = s_ControllerMappings[i];
   791     while ( pMappingString )
   792     {
   793         SDL_GameControllerAddMapping( pMappingString );
   794 
   795         i++;
   796         pMappingString = s_ControllerMappings[i];
   797     }
   798 
   799     /* load in any user supplied config */
   800     SDL_GameControllerLoadHints();
   801 
   802     /* watch for joy events and fire controller ones if needed */
   803     SDL_AddEventWatch( SDL_GameControllerEventWatcher, NULL );
   804 
   805     return (0);
   806 }
   807 
   808 
   809 /*
   810  * Get the implementation dependent name of a controller
   811  */
   812 const char *
   813 SDL_GameControllerNameForIndex(int device_index)
   814 {
   815     ControllerMapping_t *pSupportedController =  SDL_PrivateGetControllerMapping(device_index);
   816     if ( pSupportedController )
   817     {
   818         return pSupportedController->name;
   819     }
   820     return NULL;
   821 }
   822 
   823 
   824 /*
   825  * Return 1 if the joystick at this device index is a supported controller
   826  */
   827 SDL_bool
   828 SDL_IsGameController(int device_index)
   829 {
   830     ControllerMapping_t *pSupportedController =  SDL_PrivateGetControllerMapping(device_index);
   831     if ( pSupportedController )
   832     {
   833         return SDL_TRUE;
   834     }
   835 
   836     return SDL_FALSE;
   837 }
   838 
   839 /*
   840  * Open a controller for use - the index passed as an argument refers to
   841  * the N'th controller on the system.  This index is the value which will
   842  * identify this controller in future controller events.
   843  *
   844  * This function returns a controller identifier, or NULL if an error occurred.
   845  */
   846 SDL_GameController *
   847 SDL_GameControllerOpen(int device_index)
   848 {
   849     SDL_GameController *gamecontroller;
   850     SDL_GameController *gamecontrollerlist;
   851     ControllerMapping_t *pSupportedController = NULL;
   852 
   853     if ((device_index < 0) || (device_index >= SDL_NumJoysticks())) {
   854         SDL_SetError("There are %d joysticks available", SDL_NumJoysticks());
   855         return (NULL);
   856     }
   857 
   858     gamecontrollerlist = SDL_gamecontrollers;
   859     /* If the controller is already open, return it */
   860     while ( gamecontrollerlist )
   861     {
   862         if ( SDL_SYS_GetInstanceIdOfDeviceIndex(device_index) == gamecontrollerlist->joystick->instance_id ) {
   863                 gamecontroller = gamecontrollerlist;
   864                 ++gamecontroller->ref_count;
   865                 return (gamecontroller);
   866         }
   867         gamecontrollerlist = gamecontrollerlist->next;
   868     }
   869 
   870     /* Find a controller mapping */
   871     pSupportedController =  SDL_PrivateGetControllerMapping(device_index);
   872     if ( !pSupportedController ) {
   873         SDL_SetError("Couldn't find mapping for device (%d)", device_index );
   874         return (NULL);
   875     }
   876 
   877     /* Create and initialize the joystick */
   878     gamecontroller = (SDL_GameController *) SDL_malloc((sizeof *gamecontroller));
   879     if (gamecontroller == NULL) {
   880         SDL_OutOfMemory();
   881         return NULL;
   882     }
   883 
   884     SDL_memset(gamecontroller, 0, (sizeof *gamecontroller));
   885     gamecontroller->joystick = SDL_JoystickOpen(device_index);
   886     if ( !gamecontroller->joystick ) {
   887         SDL_free(gamecontroller);
   888         return NULL;
   889     }
   890 
   891     SDL_PrivateLoadButtonMapping( &gamecontroller->mapping, pSupportedController->guid, pSupportedController->name, pSupportedController->mapping );
   892 
   893     /* Add joystick to list */
   894     ++gamecontroller->ref_count;
   895     /* Link the joystick in the list */
   896     gamecontroller->next = SDL_gamecontrollers;
   897     SDL_gamecontrollers = gamecontroller;
   898 
   899     SDL_SYS_JoystickUpdate( gamecontroller->joystick );
   900 
   901     return (gamecontroller);
   902 }
   903 
   904 /*
   905  * Manually pump for controller updates.
   906  */
   907 void
   908 SDL_GameControllerUpdate(void)
   909 {
   910     /* Just for API completeness; the joystick API does all the work. */
   911     SDL_JoystickUpdate();
   912 }
   913 
   914 
   915 /*
   916  * Get the current state of an axis control on a controller
   917  */
   918 Sint16
   919 SDL_GameControllerGetAxis(SDL_GameController * gamecontroller, SDL_GameControllerAxis axis)
   920 {
   921     if ( !gamecontroller )
   922         return 0;
   923 
   924     if (gamecontroller->mapping.axes[axis] >= 0 )
   925     {
   926         Sint16 value = ( SDL_JoystickGetAxis( gamecontroller->joystick, gamecontroller->mapping.axes[axis]) );
   927         switch (axis)
   928         {
   929             case SDL_CONTROLLER_AXIS_TRIGGERLEFT:
   930             case SDL_CONTROLLER_AXIS_TRIGGERRIGHT:
   931                 /* Shift it to be 0 - 32767. */
   932                 value = value / 2 + 16384;
   933             default:
   934                 break;
   935         }
   936         return value;
   937     }
   938     else if (gamecontroller->mapping.buttonasaxis[axis] >= 0 )
   939     {
   940         Uint8 value;
   941         value = SDL_JoystickGetButton( gamecontroller->joystick, gamecontroller->mapping.buttonasaxis[axis] );
   942         if ( value > 0 )
   943             return 32767;
   944         return 0;
   945     }
   946     return 0;
   947 }
   948 
   949 
   950 /*
   951  * Get the current state of a button on a controller
   952  */
   953 Uint8
   954 SDL_GameControllerGetButton(SDL_GameController * gamecontroller, SDL_GameControllerButton button)
   955 {
   956     if ( !gamecontroller )
   957         return 0;
   958 
   959     if ( gamecontroller->mapping.buttons[button] >= 0 )
   960     {
   961         return ( SDL_JoystickGetButton( gamecontroller->joystick, gamecontroller->mapping.buttons[button] ) );
   962     }
   963     else if ( gamecontroller->mapping.axesasbutton[button] >= 0 )
   964     {
   965         Sint16 value;
   966         value = SDL_JoystickGetAxis( gamecontroller->joystick, gamecontroller->mapping.axesasbutton[button] );
   967         if ( ABS(value) > 32768/2 )
   968             return 1;
   969         return 0;
   970     }
   971     else if ( gamecontroller->mapping.hatasbutton[button].hat >= 0 )
   972     {
   973         Uint8 value;
   974         value = SDL_JoystickGetHat( gamecontroller->joystick, gamecontroller->mapping.hatasbutton[button].hat );
   975 
   976         if ( value & gamecontroller->mapping.hatasbutton[button].mask )
   977             return 1;
   978         return 0;
   979     }
   980 
   981     return 0;
   982 }
   983 
   984 /*
   985  * Return if the joystick in question is currently attached to the system,
   986  *  \return 0 if not plugged in, 1 if still present.
   987  */
   988 SDL_bool
   989 SDL_GameControllerGetAttached( SDL_GameController * gamecontroller )
   990 {
   991     if ( !gamecontroller )
   992         return SDL_FALSE;
   993 
   994     return SDL_JoystickGetAttached(gamecontroller->joystick);
   995 }
   996 
   997 
   998 /*
   999  * Get the number of multi-dimensional axis controls on a joystick
  1000  */
  1001 const char *
  1002 SDL_GameControllerName(SDL_GameController * gamecontroller)
  1003 {
  1004     if ( !gamecontroller )
  1005         return NULL;
  1006 
  1007     return (gamecontroller->mapping.name);
  1008 }
  1009 
  1010 
  1011 /*
  1012  * Get the joystick for this controller
  1013  */
  1014 SDL_Joystick *SDL_GameControllerGetJoystick(SDL_GameController * gamecontroller)
  1015 {
  1016     if ( !gamecontroller )
  1017         return NULL;
  1018 
  1019     return gamecontroller->joystick;
  1020 }
  1021 
  1022 /**
  1023  * Get the SDL joystick layer binding for this controller axis mapping
  1024  */
  1025 SDL_GameControllerButtonBind SDL_GameControllerGetBindForAxis(SDL_GameController * gamecontroller, SDL_GameControllerAxis axis)
  1026 {
  1027     SDL_GameControllerButtonBind bind;
  1028     SDL_memset( &bind, 0x0, sizeof(bind) );
  1029 
  1030     if ( !gamecontroller || axis == SDL_CONTROLLER_AXIS_INVALID )
  1031         return bind;
  1032 
  1033     if (gamecontroller->mapping.axes[axis] >= 0 )
  1034     {
  1035         bind.bindType = SDL_CONTROLLER_BINDTYPE_AXIS;
  1036         bind.value.button = gamecontroller->mapping.axes[axis];
  1037     }
  1038     else if (gamecontroller->mapping.buttonasaxis[axis] >= 0 )
  1039     {
  1040         bind.bindType = SDL_CONTROLLER_BINDTYPE_BUTTON;
  1041         bind.value.button = gamecontroller->mapping.buttonasaxis[axis];
  1042     }
  1043 
  1044     return bind;
  1045 }
  1046 
  1047 
  1048 /**
  1049  * Get the SDL joystick layer binding for this controller button mapping
  1050  */
  1051 SDL_GameControllerButtonBind SDL_GameControllerGetBindForButton(SDL_GameController * gamecontroller, SDL_GameControllerButton button)
  1052 {
  1053     SDL_GameControllerButtonBind bind;
  1054     SDL_memset( &bind, 0x0, sizeof(bind) );
  1055 
  1056     if ( !gamecontroller || button == SDL_CONTROLLER_BUTTON_INVALID )
  1057         return bind;
  1058 
  1059     if ( gamecontroller->mapping.buttons[button] >= 0 )
  1060     {
  1061         bind.bindType = SDL_CONTROLLER_BINDTYPE_BUTTON;
  1062         bind.value.button = gamecontroller->mapping.buttons[button];
  1063     }
  1064     else if ( gamecontroller->mapping.axesasbutton[button] >= 0 )
  1065     {
  1066         bind.bindType = SDL_CONTROLLER_BINDTYPE_AXIS;
  1067         bind.value.axis = gamecontroller->mapping.axesasbutton[button];
  1068     }
  1069     else if ( gamecontroller->mapping.hatasbutton[button].hat >= 0 )
  1070     {
  1071         bind.bindType = SDL_CONTROLLER_BINDTYPE_HAT;
  1072         bind.value.hat.hat = gamecontroller->mapping.hatasbutton[button].hat;
  1073         bind.value.hat.hat_mask = gamecontroller->mapping.hatasbutton[button].mask;
  1074     }
  1075 
  1076     return bind;
  1077 }
  1078 
  1079 
  1080 /*
  1081  * Close a joystick previously opened with SDL_JoystickOpen()
  1082  */
  1083 void
  1084 SDL_GameControllerClose(SDL_GameController * gamecontroller)
  1085 {
  1086     SDL_GameController *gamecontrollerlist, *gamecontrollerlistprev;
  1087 
  1088     if ( !gamecontroller )
  1089         return;
  1090 
  1091     /* First decrement ref count */
  1092     if (--gamecontroller->ref_count > 0) {
  1093         return;
  1094     }
  1095 
  1096     SDL_JoystickClose( gamecontroller->joystick );
  1097 
  1098     gamecontrollerlist = SDL_gamecontrollers;
  1099     gamecontrollerlistprev = NULL;
  1100     while ( gamecontrollerlist )
  1101     {
  1102         if (gamecontroller == gamecontrollerlist)
  1103         {
  1104             if ( gamecontrollerlistprev )
  1105             {
  1106                 /* unlink this entry */
  1107                 gamecontrollerlistprev->next = gamecontrollerlist->next;
  1108             }
  1109             else
  1110             {
  1111                 SDL_gamecontrollers = gamecontroller->next;
  1112             }
  1113 
  1114             break;
  1115         }
  1116         gamecontrollerlistprev = gamecontrollerlist;
  1117         gamecontrollerlist = gamecontrollerlist->next;
  1118     }
  1119 
  1120     SDL_free(gamecontroller);
  1121 }
  1122 
  1123 
  1124 /*
  1125  * Quit the controller subsystem
  1126  */
  1127 void
  1128 SDL_GameControllerQuit(void)
  1129 {
  1130     ControllerMapping_t *pControllerMap;
  1131     while ( SDL_gamecontrollers )
  1132     {
  1133         SDL_gamecontrollers->ref_count = 1;
  1134         SDL_GameControllerClose(SDL_gamecontrollers);
  1135     }
  1136 
  1137     while ( s_pSupportedControllers )
  1138     {
  1139         pControllerMap = s_pSupportedControllers;
  1140         s_pSupportedControllers = s_pSupportedControllers->next;
  1141         SDL_free( pControllerMap->name );
  1142         SDL_free( pControllerMap );
  1143     }
  1144 
  1145     SDL_DelEventWatch( SDL_GameControllerEventWatcher, NULL );
  1146 
  1147 }
  1148 
  1149 /*
  1150  * Event filter to transform joystick events into appropriate game controller ones
  1151  */
  1152 int
  1153 SDL_PrivateGameControllerAxis(SDL_GameController * gamecontroller, SDL_GameControllerAxis axis, Sint16 value)
  1154 {
  1155     int posted;
  1156 
  1157     /* translate the event, if desired */
  1158     posted = 0;
  1159 #if !SDL_EVENTS_DISABLED
  1160     if (SDL_GetEventState(SDL_CONTROLLERAXISMOTION) == SDL_ENABLE) {
  1161         SDL_Event event;
  1162         event.type = SDL_CONTROLLERAXISMOTION;
  1163         event.caxis.which = gamecontroller->joystick->instance_id;
  1164         event.caxis.axis = axis;
  1165         event.caxis.value = value;
  1166         posted = SDL_PushEvent(&event) == 1;
  1167     }
  1168 #endif /* !SDL_EVENTS_DISABLED */
  1169     return (posted);
  1170 }
  1171 
  1172 
  1173 /*
  1174  * Event filter to transform joystick events into appropriate game controller ones
  1175  */
  1176 int
  1177 SDL_PrivateGameControllerButton(SDL_GameController * gamecontroller, SDL_GameControllerButton button, Uint8 state)
  1178 {
  1179     int posted;
  1180 #if !SDL_EVENTS_DISABLED
  1181     SDL_Event event;
  1182 
  1183     if ( button == SDL_CONTROLLER_BUTTON_INVALID )
  1184         return (0);
  1185 
  1186     switch (state) {
  1187     case SDL_PRESSED:
  1188         event.type = SDL_CONTROLLERBUTTONDOWN;
  1189         break;
  1190     case SDL_RELEASED:
  1191         event.type = SDL_CONTROLLERBUTTONUP;
  1192         break;
  1193     default:
  1194         /* Invalid state -- bail */
  1195         return (0);
  1196     }
  1197 #endif /* !SDL_EVENTS_DISABLED */
  1198 
  1199     /* translate the event, if desired */
  1200     posted = 0;
  1201 #if !SDL_EVENTS_DISABLED
  1202     if (SDL_GetEventState(event.type) == SDL_ENABLE) {
  1203         event.cbutton.which = gamecontroller->joystick->instance_id;
  1204         event.cbutton.button = button;
  1205         event.cbutton.state = state;
  1206         posted = SDL_PushEvent(&event) == 1;
  1207     }
  1208 #endif /* !SDL_EVENTS_DISABLED */
  1209     return (posted);
  1210 }
  1211 
  1212 /*
  1213  * Turn off controller events
  1214  */
  1215 int
  1216 SDL_GameControllerEventState(int state)
  1217 {
  1218 #if SDL_EVENTS_DISABLED
  1219     return SDL_IGNORE;
  1220 #else
  1221     const Uint32 event_list[] = {
  1222         SDL_CONTROLLERAXISMOTION, SDL_CONTROLLERBUTTONDOWN, SDL_CONTROLLERBUTTONUP,
  1223         SDL_CONTROLLERDEVICEADDED, SDL_CONTROLLERDEVICEREMOVED, SDL_CONTROLLERDEVICEREMAPPED,
  1224     };
  1225     unsigned int i;
  1226 
  1227     switch (state) {
  1228     case SDL_QUERY:
  1229         state = SDL_IGNORE;
  1230         for (i = 0; i < SDL_arraysize(event_list); ++i) {
  1231             state = SDL_EventState(event_list[i], SDL_QUERY);
  1232             if (state == SDL_ENABLE) {
  1233                 break;
  1234             }
  1235         }
  1236         break;
  1237     default:
  1238         for (i = 0; i < SDL_arraysize(event_list); ++i) {
  1239             SDL_EventState(event_list[i], state);
  1240         }
  1241         break;
  1242     }
  1243     return (state);
  1244 #endif /* SDL_EVENTS_DISABLED */
  1245 }
  1246 
  1247 /* vi: set ts=4 sw=4 expandtab: */