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