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