| /* |
| Simple DirectMedia Layer |
| Copyright (C) 1997-2014 Sam Lantinga <[email protected]> |
| |
| This software is provided 'as-is', without any express or implied |
| warranty. In no event will the authors be held liable for any damages |
| arising from the use of this software. |
| |
| Permission is granted to anyone to use this software for any purpose, |
| including commercial applications, and to alter it and redistribute it |
| freely, subject to the following restrictions: |
| |
| 1. The origin of this software must not be misrepresented; you must not |
| claim that you wrote the original software. If you use this software |
| in a product, an acknowledgment in the product documentation would be |
| appreciated but is not required. |
| 2. Altered source versions must be plainly marked as such, and must not be |
| misrepresented as being the original software. |
| 3. This notice may not be removed or altered from any source distribution. |
| */ |
| #include "../SDL_internal.h" |
| |
| /* This is the game controller API for Simple DirectMedia Layer */ |
| |
| #include "SDL_events.h" |
| #include "SDL_assert.h" |
| #include "SDL_sysjoystick.h" |
| #include "SDL_hints.h" |
| #include "SDL_gamecontrollerdb.h" |
| |
| #if !SDL_EVENTS_DISABLED |
| #include "../events/SDL_events_c.h" |
| #endif |
| #define ABS(_x) ((_x) < 0 ? -(_x) : (_x)) |
| |
| #define SDL_CONTROLLER_PLATFORM_FIELD "platform:" |
| |
| /* a list of currently opened game controllers */ |
| static SDL_GameController *SDL_gamecontrollers = NULL; |
| |
| /* keep track of the hat and mask value that transforms this hat movement into a button/axis press */ |
| struct _SDL_HatMapping |
| { |
| int hat; |
| Uint8 mask; |
| }; |
| |
| #define k_nMaxReverseEntries 20 |
| |
| /** |
| * We are encoding the "HAT" as 0xhm. where h == hat ID and m == mask |
| * MAX 4 hats supported |
| */ |
| #define k_nMaxHatEntries 0x3f + 1 |
| |
| /* our in memory mapping db between joystick objects and controller mappings */ |
| struct _SDL_ControllerMapping |
| { |
| SDL_JoystickGUID guid; |
| const char *name; |
| |
| /* mapping of axis/button id to controller version */ |
| int axes[SDL_CONTROLLER_AXIS_MAX]; |
| int buttonasaxis[SDL_CONTROLLER_AXIS_MAX]; |
| |
| int buttons[SDL_CONTROLLER_BUTTON_MAX]; |
| int axesasbutton[SDL_CONTROLLER_BUTTON_MAX]; |
| struct _SDL_HatMapping hatasbutton[SDL_CONTROLLER_BUTTON_MAX]; |
| |
| /* reverse mapping, joystick indices to buttons */ |
| SDL_GameControllerAxis raxes[k_nMaxReverseEntries]; |
| SDL_GameControllerAxis rbuttonasaxis[k_nMaxReverseEntries]; |
| |
| SDL_GameControllerButton rbuttons[k_nMaxReverseEntries]; |
| SDL_GameControllerButton raxesasbutton[k_nMaxReverseEntries]; |
| SDL_GameControllerButton rhatasbutton[k_nMaxHatEntries]; |
| |
| }; |
| |
| |
| /* our hard coded list of mapping support */ |
| typedef struct _ControllerMapping_t |
| { |
| SDL_JoystickGUID guid; |
| char *name; |
| char *mapping; |
| struct _ControllerMapping_t *next; |
| } ControllerMapping_t; |
| |
| static ControllerMapping_t *s_pSupportedControllers = NULL; |
| #if defined(SDL_JOYSTICK_DINPUT) || defined(SDL_JOYSTICK_XINPUT) |
| static ControllerMapping_t *s_pXInputMapping = NULL; |
| #endif |
| |
| /* The SDL game controller structure */ |
| struct _SDL_GameController |
| { |
| SDL_Joystick *joystick; /* underlying joystick device */ |
| int ref_count; |
| Uint8 hatState[4]; /* the current hat state for this controller */ |
| struct _SDL_ControllerMapping mapping; /* the mapping object for this controller */ |
| struct _SDL_GameController *next; /* pointer to next game controller we have allocated */ |
| }; |
| |
| |
| int SDL_PrivateGameControllerAxis(SDL_GameController * gamecontroller, SDL_GameControllerAxis axis, Sint16 value); |
| int SDL_PrivateGameControllerButton(SDL_GameController * gamecontroller, SDL_GameControllerButton button, Uint8 state); |
| |
| /* |
| * Event filter to fire controller events from joystick ones |
| */ |
| int SDL_GameControllerEventWatcher(void *userdata, SDL_Event * event) |
| { |
| switch( event->type ) |
| { |
| case SDL_JOYAXISMOTION: |
| { |
| SDL_GameController *controllerlist; |
| |
| if ( event->jaxis.axis >= k_nMaxReverseEntries ) break; |
| |
| controllerlist = SDL_gamecontrollers; |
| while ( controllerlist ) |
| { |
| if ( controllerlist->joystick->instance_id == event->jaxis.which ) |
| { |
| if ( controllerlist->mapping.raxes[event->jaxis.axis] >= 0 ) /* simple axis to axis, send it through */ |
| { |
| SDL_GameControllerAxis axis = controllerlist->mapping.raxes[event->jaxis.axis]; |
| Sint16 value = event->jaxis.value; |
| switch (axis) |
| { |
| case SDL_CONTROLLER_AXIS_TRIGGERLEFT: |
| case SDL_CONTROLLER_AXIS_TRIGGERRIGHT: |
| /* Shift it to be 0 - 32767. */ |
| value = value / 2 + 16384; |
| default: |
| break; |
| } |
| SDL_PrivateGameControllerAxis( controllerlist, axis, value ); |
| } |
| else if ( controllerlist->mapping.raxesasbutton[event->jaxis.axis] >= 0 ) /* simulate an axis as a button */ |
| { |
| SDL_PrivateGameControllerButton( controllerlist, controllerlist->mapping.raxesasbutton[event->jaxis.axis], ABS(event->jaxis.value) > 32768/2 ? SDL_PRESSED : SDL_RELEASED ); |
| } |
| break; |
| } |
| controllerlist = controllerlist->next; |
| } |
| } |
| break; |
| case SDL_JOYBUTTONDOWN: |
| case SDL_JOYBUTTONUP: |
| { |
| SDL_GameController *controllerlist; |
| |
| if ( event->jbutton.button >= k_nMaxReverseEntries ) break; |
| |
| controllerlist = SDL_gamecontrollers; |
| while ( controllerlist ) |
| { |
| if ( controllerlist->joystick->instance_id == event->jbutton.which ) |
| { |
| if ( controllerlist->mapping.rbuttons[event->jbutton.button] >= 0 ) /* simple button as button */ |
| { |
| SDL_PrivateGameControllerButton( controllerlist, controllerlist->mapping.rbuttons[event->jbutton.button], event->jbutton.state ); |
| } |
| else if ( controllerlist->mapping.rbuttonasaxis[event->jbutton.button] >= 0 ) /* an button pretending to be an axis */ |
| { |
| SDL_PrivateGameControllerAxis( controllerlist, controllerlist->mapping.rbuttonasaxis[event->jbutton.button], event->jbutton.state > 0 ? 32767 : 0 ); |
| } |
| break; |
| } |
| controllerlist = controllerlist->next; |
| } |
| } |
| break; |
| case SDL_JOYHATMOTION: |
| { |
| SDL_GameController *controllerlist; |
| |
| if ( event->jhat.hat >= 4 ) break; |
| |
| controllerlist = SDL_gamecontrollers; |
| while ( controllerlist ) |
| { |
| if ( controllerlist->joystick->instance_id == event->jhat.which ) |
| { |
| Uint8 bSame = controllerlist->hatState[event->jhat.hat] & event->jhat.value; |
| /* Get list of removed bits (button release) */ |
| Uint8 bChanged = controllerlist->hatState[event->jhat.hat] ^ bSame; |
| /* the hat idx in the high nibble */ |
| int bHighHat = event->jhat.hat << 4; |
| |
| if ( bChanged & SDL_HAT_DOWN ) |
| SDL_PrivateGameControllerButton( controllerlist, controllerlist->mapping.rhatasbutton[bHighHat | SDL_HAT_DOWN], SDL_RELEASED ); |
| if ( bChanged & SDL_HAT_UP ) |
| SDL_PrivateGameControllerButton( controllerlist, controllerlist->mapping.rhatasbutton[bHighHat | SDL_HAT_UP], SDL_RELEASED ); |
| if ( bChanged & SDL_HAT_LEFT ) |
| SDL_PrivateGameControllerButton( controllerlist, controllerlist->mapping.rhatasbutton[bHighHat | SDL_HAT_LEFT], SDL_RELEASED ); |
| if ( bChanged & SDL_HAT_RIGHT ) |
| SDL_PrivateGameControllerButton( controllerlist, controllerlist->mapping.rhatasbutton[bHighHat | SDL_HAT_RIGHT], SDL_RELEASED ); |
| |
| /* Get list of added bits (button press) */ |
| bChanged = event->jhat.value ^ bSame; |
| |
| if ( bChanged & SDL_HAT_DOWN ) |
| SDL_PrivateGameControllerButton( controllerlist, controllerlist->mapping.rhatasbutton[bHighHat | SDL_HAT_DOWN], SDL_PRESSED ); |
| if ( bChanged & SDL_HAT_UP ) |
| SDL_PrivateGameControllerButton( controllerlist, controllerlist->mapping.rhatasbutton[bHighHat | SDL_HAT_UP], SDL_PRESSED ); |
| if ( bChanged & SDL_HAT_LEFT ) |
| SDL_PrivateGameControllerButton( controllerlist, controllerlist->mapping.rhatasbutton[bHighHat | SDL_HAT_LEFT], SDL_PRESSED ); |
| if ( bChanged & SDL_HAT_RIGHT ) |
| SDL_PrivateGameControllerButton( controllerlist, controllerlist->mapping.rhatasbutton[bHighHat | SDL_HAT_RIGHT], SDL_PRESSED ); |
| |
| /* update our state cache */ |
| controllerlist->hatState[event->jhat.hat] = event->jhat.value; |
| |
| break; |
| } |
| controllerlist = controllerlist->next; |
| } |
| } |
| break; |
| case SDL_JOYDEVICEADDED: |
| { |
| if ( SDL_IsGameController(event->jdevice.which ) ) |
| { |
| SDL_Event deviceevent; |
| deviceevent.type = SDL_CONTROLLERDEVICEADDED; |
| deviceevent.cdevice.which = event->jdevice.which; |
| SDL_PushEvent(&deviceevent); |
| } |
| } |
| break; |
| case SDL_JOYDEVICEREMOVED: |
| { |
| SDL_GameController *controllerlist = SDL_gamecontrollers; |
| while ( controllerlist ) |
| { |
| if ( controllerlist->joystick->instance_id == event->jdevice.which ) |
| { |
| SDL_Event deviceevent; |
| deviceevent.type = SDL_CONTROLLERDEVICEREMOVED; |
| deviceevent.cdevice.which = event->jdevice.which; |
| SDL_PushEvent(&deviceevent); |
| break; |
| } |
| controllerlist = controllerlist->next; |
| } |
| } |
| break; |
| default: |
| break; |
| } |
| |
| return 1; |
| } |
| |
| /* |
| * Helper function to scan the mappings database for a controller with the specified GUID |
| */ |
| ControllerMapping_t *SDL_PrivateGetControllerMappingForGUID(SDL_JoystickGUID *guid) |
| { |
| ControllerMapping_t *pSupportedController = s_pSupportedControllers; |
| while ( pSupportedController ) |
| { |
| if ( !SDL_memcmp( guid, &pSupportedController->guid, sizeof(*guid) ) ) |
| { |
| return pSupportedController; |
| } |
| pSupportedController = pSupportedController->next; |
| } |
| return NULL; |
| } |
| |
| /* |
| * Helper function to determine pre-calculated offset to certain joystick mappings |
| */ |
| ControllerMapping_t *SDL_PrivateGetControllerMapping(int device_index) |
| { |
| #if defined(SDL_JOYSTICK_DINPUT) || defined(SDL_JOYSTICK_XINPUT) |
| if ( SDL_SYS_IsXInputDeviceIndex(device_index) && s_pXInputMapping ) |
| { |
| return s_pXInputMapping; |
| } |
| else |
| #endif |
| { |
| SDL_JoystickGUID jGUID = SDL_JoystickGetDeviceGUID( device_index ); |
| return SDL_PrivateGetControllerMappingForGUID(&jGUID); |
| } |
| } |
| |
| static const char* map_StringForControllerAxis[] = { |
| "leftx", |
| "lefty", |
| "rightx", |
| "righty", |
| "lefttrigger", |
| "righttrigger", |
| NULL |
| }; |
| |
| /* |
| * convert a string to its enum equivalent |
| */ |
| SDL_GameControllerAxis SDL_GameControllerGetAxisFromString( const char *pchString ) |
| { |
| int entry; |
| if ( !pchString || !pchString[0] ) |
| return SDL_CONTROLLER_AXIS_INVALID; |
| |
| for ( entry = 0; map_StringForControllerAxis[entry]; ++entry) |
| { |
| if ( !SDL_strcasecmp( pchString, map_StringForControllerAxis[entry] ) ) |
| return entry; |
| } |
| return SDL_CONTROLLER_AXIS_INVALID; |
| } |
| |
| /* |
| * convert an enum to its string equivalent |
| */ |
| const char* SDL_GameControllerGetStringForAxis( SDL_GameControllerAxis axis ) |
| { |
| if (axis > SDL_CONTROLLER_AXIS_INVALID && axis < SDL_CONTROLLER_AXIS_MAX) |
| { |
| return map_StringForControllerAxis[axis]; |
| } |
| return NULL; |
| } |
| |
| static const char* map_StringForControllerButton[] = { |
| "a", |
| "b", |
| "x", |
| "y", |
| "back", |
| "guide", |
| "start", |
| "leftstick", |
| "rightstick", |
| "leftshoulder", |
| "rightshoulder", |
| "dpup", |
| "dpdown", |
| "dpleft", |
| "dpright", |
| NULL |
| }; |
| |
| /* |
| * convert a string to its enum equivalent |
| */ |
| SDL_GameControllerButton SDL_GameControllerGetButtonFromString( const char *pchString ) |
| { |
| int entry; |
| if ( !pchString || !pchString[0] ) |
| return SDL_CONTROLLER_BUTTON_INVALID; |
| |
| for ( entry = 0; map_StringForControllerButton[entry]; ++entry) |
| { |
| if ( !SDL_strcasecmp( pchString, map_StringForControllerButton[entry] ) ) |
| return entry; |
| } |
| return SDL_CONTROLLER_BUTTON_INVALID; |
| } |
| |
| /* |
| * convert an enum to its string equivalent |
| */ |
| const char* SDL_GameControllerGetStringForButton( SDL_GameControllerButton axis ) |
| { |
| if (axis > SDL_CONTROLLER_BUTTON_INVALID && axis < SDL_CONTROLLER_BUTTON_MAX) |
| { |
| return map_StringForControllerButton[axis]; |
| } |
| return NULL; |
| } |
| |
| /* |
| * given a controller button name and a joystick name update our mapping structure with it |
| */ |
| void SDL_PrivateGameControllerParseButton( const char *szGameButton, const char *szJoystickButton, struct _SDL_ControllerMapping *pMapping ) |
| { |
| int iSDLButton = 0; |
| SDL_GameControllerButton button; |
| SDL_GameControllerAxis axis; |
| button = SDL_GameControllerGetButtonFromString( szGameButton ); |
| axis = SDL_GameControllerGetAxisFromString( szGameButton ); |
| iSDLButton = SDL_atoi( &szJoystickButton[1] ); |
| |
| if ( szJoystickButton[0] == 'a' ) |
| { |
| if ( iSDLButton >= k_nMaxReverseEntries ) |
| { |
| SDL_SetError("Axis index too large: %d", iSDLButton ); |
| return; |
| } |
| if ( axis != SDL_CONTROLLER_AXIS_INVALID ) |
| { |
| pMapping->axes[ axis ] = iSDLButton; |
| pMapping->raxes[ iSDLButton ] = axis; |
| } |
| else if ( button != SDL_CONTROLLER_BUTTON_INVALID ) |
| { |
| pMapping->axesasbutton[ button ] = iSDLButton; |
| pMapping->raxesasbutton[ iSDLButton ] = button; |
| } |
| else |
| { |
| SDL_assert( !"How did we get here?" ); |
| } |
| |
| } |
| else if ( szJoystickButton[0] == 'b' ) |
| { |
| if ( iSDLButton >= k_nMaxReverseEntries ) |
| { |
| SDL_SetError("Button index too large: %d", iSDLButton ); |
| return; |
| } |
| if ( button != SDL_CONTROLLER_BUTTON_INVALID ) |
| { |
| pMapping->buttons[ button ] = iSDLButton; |
| pMapping->rbuttons[ iSDLButton ] = button; |
| } |
| else if ( axis != SDL_CONTROLLER_AXIS_INVALID ) |
| { |
| pMapping->buttonasaxis[ axis ] = iSDLButton; |
| pMapping->rbuttonasaxis[ iSDLButton ] = axis; |
| } |
| else |
| { |
| SDL_assert( !"How did we get here?" ); |
| } |
| } |
| else if ( szJoystickButton[0] == 'h' ) |
| { |
| int hat = SDL_atoi( &szJoystickButton[1] ); |
| int mask = SDL_atoi( &szJoystickButton[3] ); |
| if (hat >= 4) { |
| SDL_SetError("Hat index too large: %d", iSDLButton ); |
| } |
| |
| if ( button != SDL_CONTROLLER_BUTTON_INVALID ) |
| { |
| int ridx; |
| pMapping->hatasbutton[ button ].hat = hat; |
| pMapping->hatasbutton[ button ].mask = mask; |
| ridx = (hat << 4) | mask; |
| pMapping->rhatasbutton[ ridx ] = button; |
| } |
| else if ( axis != SDL_CONTROLLER_AXIS_INVALID ) |
| { |
| SDL_assert( !"Support hat as axis" ); |
| } |
| else |
| { |
| SDL_assert( !"How did we get here?" ); |
| } |
| } |
| } |
| |
| |
| /* |
| * given a controller mapping string update our mapping object |
| */ |
| static void |
| SDL_PrivateGameControllerParseControllerConfigString( struct _SDL_ControllerMapping *pMapping, const char *pchString ) |
| { |
| char szGameButton[20]; |
| char szJoystickButton[20]; |
| SDL_bool bGameButton = SDL_TRUE; |
| int i = 0; |
| const char *pchPos = pchString; |
| |
| SDL_memset( szGameButton, 0x0, sizeof(szGameButton) ); |
| SDL_memset( szJoystickButton, 0x0, sizeof(szJoystickButton) ); |
| |
| while ( pchPos && *pchPos ) |
| { |
| if ( *pchPos == ':' ) |
| { |
| i = 0; |
| bGameButton = SDL_FALSE; |
| } |
| else if ( *pchPos == ' ' ) |
| { |
| |
| } |
| else if ( *pchPos == ',' ) |
| { |
| i = 0; |
| bGameButton = SDL_TRUE; |
| SDL_PrivateGameControllerParseButton( szGameButton, szJoystickButton, pMapping ); |
| SDL_memset( szGameButton, 0x0, sizeof(szGameButton) ); |
| SDL_memset( szJoystickButton, 0x0, sizeof(szJoystickButton) ); |
| |
| } |
| else if ( bGameButton ) |
| { |
| if ( i >= sizeof(szGameButton)) |
| { |
| SDL_SetError( "Button name too large: %s", szGameButton ); |
| return; |
| } |
| szGameButton[i] = *pchPos; |
| i++; |
| } |
| else |
| { |
| if ( i >= sizeof(szJoystickButton)) |
| { |
| SDL_SetError( "Joystick button name too large: %s", szJoystickButton ); |
| return; |
| } |
| szJoystickButton[i] = *pchPos; |
| i++; |
| } |
| pchPos++; |
| } |
| |
| SDL_PrivateGameControllerParseButton( szGameButton, szJoystickButton, pMapping ); |
| |
| } |
| |
| /* |
| * Make a new button mapping struct |
| */ |
| void SDL_PrivateLoadButtonMapping( struct _SDL_ControllerMapping *pMapping, SDL_JoystickGUID guid, const char *pchName, const char *pchMapping ) |
| { |
| int j; |
| |
| pMapping->guid = guid; |
| pMapping->name = pchName; |
| |
| /* set all the button mappings to non defaults */ |
| for ( j = 0; j < SDL_CONTROLLER_AXIS_MAX; j++ ) |
| { |
| pMapping->axes[j] = -1; |
| pMapping->buttonasaxis[j] = -1; |
| } |
| for ( j = 0; j < SDL_CONTROLLER_BUTTON_MAX; j++ ) |
| { |
| pMapping->buttons[j] = -1; |
| pMapping->axesasbutton[j] = -1; |
| pMapping->hatasbutton[j].hat = -1; |
| } |
| |
| for ( j = 0; j < k_nMaxReverseEntries; j++ ) |
| { |
| pMapping->raxes[j] = SDL_CONTROLLER_AXIS_INVALID; |
| pMapping->rbuttonasaxis[j] = SDL_CONTROLLER_AXIS_INVALID; |
| pMapping->rbuttons[j] = SDL_CONTROLLER_BUTTON_INVALID; |
| pMapping->raxesasbutton[j] = SDL_CONTROLLER_BUTTON_INVALID; |
| } |
| |
| for (j = 0; j < k_nMaxHatEntries; j++) |
| { |
| pMapping->rhatasbutton[j] = SDL_CONTROLLER_BUTTON_INVALID; |
| } |
| |
| SDL_PrivateGameControllerParseControllerConfigString( pMapping, pchMapping ); |
| } |
| |
| |
| /* |
| * grab the guid string from a mapping string |
| */ |
| char *SDL_PrivateGetControllerGUIDFromMappingString( const char *pMapping ) |
| { |
| const char *pFirstComma = SDL_strchr( pMapping, ',' ); |
| if ( pFirstComma ) |
| { |
| char *pchGUID = SDL_malloc( pFirstComma - pMapping + 1 ); |
| if ( !pchGUID ) |
| { |
| SDL_OutOfMemory(); |
| return NULL; |
| } |
| SDL_memcpy( pchGUID, pMapping, pFirstComma - pMapping ); |
| pchGUID[ pFirstComma - pMapping ] = 0; |
| return pchGUID; |
| } |
| return NULL; |
| } |
| |
| |
| /* |
| * grab the name string from a mapping string |
| */ |
| char *SDL_PrivateGetControllerNameFromMappingString( const char *pMapping ) |
| { |
| const char *pFirstComma, *pSecondComma; |
| char *pchName; |
| |
| pFirstComma = SDL_strchr( pMapping, ',' ); |
| if ( !pFirstComma ) |
| return NULL; |
| |
| pSecondComma = SDL_strchr( pFirstComma + 1, ',' ); |
| if ( !pSecondComma ) |
| return NULL; |
| |
| pchName = SDL_malloc( pSecondComma - pFirstComma ); |
| if ( !pchName ) |
| { |
| SDL_OutOfMemory(); |
| return NULL; |
| } |
| SDL_memcpy( pchName, pFirstComma + 1, pSecondComma - pFirstComma ); |
| pchName[ pSecondComma - pFirstComma - 1 ] = 0; |
| return pchName; |
| } |
| |
| |
| /* |
| * grab the button mapping string from a mapping string |
| */ |
| char *SDL_PrivateGetControllerMappingFromMappingString( const char *pMapping ) |
| { |
| const char *pFirstComma, *pSecondComma; |
| |
| pFirstComma = SDL_strchr( pMapping, ',' ); |
| if ( !pFirstComma ) |
| return NULL; |
| |
| pSecondComma = SDL_strchr( pFirstComma + 1, ',' ); |
| if ( !pSecondComma ) |
| return NULL; |
| |
| return SDL_strdup(pSecondComma + 1); /* mapping is everything after the 3rd comma */ |
| } |
| |
| void SDL_PrivateGameControllerRefreshMapping( ControllerMapping_t *pControllerMapping ) |
| { |
| SDL_GameController *gamecontrollerlist = SDL_gamecontrollers; |
| while ( gamecontrollerlist ) |
| { |
| if ( !SDL_memcmp( &gamecontrollerlist->mapping.guid, &pControllerMapping->guid, sizeof(pControllerMapping->guid) ) ) |
| { |
| SDL_Event event; |
| event.type = SDL_CONTROLLERDEVICEREMAPPED; |
| event.cdevice.which = gamecontrollerlist->joystick->instance_id; |
| SDL_PushEvent(&event); |
| |
| /* Not really threadsafe. Should this lock access within SDL_GameControllerEventWatcher? */ |
| SDL_PrivateLoadButtonMapping(&gamecontrollerlist->mapping, pControllerMapping->guid, pControllerMapping->name, pControllerMapping->mapping); |
| } |
| |
| gamecontrollerlist = gamecontrollerlist->next; |
| } |
| } |
| |
| /* |
| * Add or update an entry into the Mappings Database |
| */ |
| int |
| SDL_GameControllerAddMappingsFromRW( SDL_RWops * rw, int freerw ) |
| { |
| const char *platform = SDL_GetPlatform(); |
| int controllers = 0; |
| char *buf, *line, *line_end, *tmp, *comma, line_platform[64]; |
| size_t db_size, platform_len; |
| |
| if (rw == NULL) { |
| return SDL_SetError("Invalid RWops"); |
| } |
| db_size = (size_t)SDL_RWsize(rw); |
| |
| buf = (char *)SDL_malloc(db_size + 1); |
| if (buf == NULL) { |
| if (freerw) { |
| SDL_RWclose(rw); |
| } |
| return SDL_SetError("Could allocate space to not read DB into memory"); |
| } |
| |
| if (SDL_RWread(rw, buf, db_size, 1) != 1) { |
| if (freerw) { |
| SDL_RWclose(rw); |
| } |
| SDL_free(buf); |
| return SDL_SetError("Could not read DB"); |
| } |
| |
| if (freerw) { |
| SDL_RWclose(rw); |
| } |
| |
| buf[db_size] = '\0'; |
| line = buf; |
| |
| while (line < buf + db_size) { |
| line_end = SDL_strchr( line, '\n' ); |
| if (line_end != NULL) { |
| *line_end = '\0'; |
| } |
| else { |
| line_end = buf + db_size; |
| } |
| |
| /* Extract and verify the platform */ |
| tmp = SDL_strstr(line, SDL_CONTROLLER_PLATFORM_FIELD); |
| if ( tmp != NULL ) { |
| tmp += SDL_strlen(SDL_CONTROLLER_PLATFORM_FIELD); |
| comma = SDL_strchr(tmp, ','); |
| if (comma != NULL) { |
| platform_len = comma - tmp + 1; |
| if (platform_len + 1 < SDL_arraysize(line_platform)) { |
| SDL_strlcpy(line_platform, tmp, platform_len); |
| if(SDL_strncasecmp(line_platform, platform, platform_len) == 0 |
| && SDL_GameControllerAddMapping(line) > 0) { |
| controllers++; |
| } |
| } |
| } |
| } |
| |
| line = line_end + 1; |
| } |
| |
| SDL_free(buf); |
| return controllers; |
| } |
| |
| /* |
| * Add or update an entry into the Mappings Database |
| */ |
| int |
| SDL_GameControllerAddMapping( const char *mappingString ) |
| { |
| char *pchGUID; |
| char *pchName; |
| char *pchMapping; |
| SDL_JoystickGUID jGUID; |
| ControllerMapping_t *pControllerMapping; |
| #if defined(SDL_JOYSTICK_DINPUT) || defined(SDL_JOYSTICK_XINPUT) |
| SDL_bool is_xinput_mapping = SDL_FALSE; |
| #endif |
| |
| pchGUID = SDL_PrivateGetControllerGUIDFromMappingString( mappingString ); |
| if (!pchGUID) { |
| return SDL_SetError("Couldn't parse GUID from %s", mappingString); |
| } |
| #if defined(SDL_JOYSTICK_DINPUT) || defined(SDL_JOYSTICK_XINPUT) |
| if ( !SDL_strcasecmp( pchGUID, "xinput" ) ) { |
| is_xinput_mapping = SDL_TRUE; |
| } |
| #endif |
| jGUID = SDL_JoystickGetGUIDFromString(pchGUID); |
| SDL_free(pchGUID); |
| |
| pchName = SDL_PrivateGetControllerNameFromMappingString( mappingString ); |
| if (!pchName) { |
| return SDL_SetError("Couldn't parse name from %s", mappingString); |
| } |
| |
| pchMapping = SDL_PrivateGetControllerMappingFromMappingString( mappingString ); |
| if (!pchMapping) { |
| SDL_free( pchName ); |
| return SDL_SetError("Couldn't parse %s", mappingString); |
| } |
| |
| pControllerMapping = SDL_PrivateGetControllerMappingForGUID(&jGUID); |
| |
| if (pControllerMapping) { |
| /* Update existing mapping */ |
| SDL_free( pControllerMapping->name ); |
| pControllerMapping->name = pchName; |
| SDL_free( pControllerMapping->mapping ); |
| pControllerMapping->mapping = pchMapping; |
| /* refresh open controllers */ |
| SDL_PrivateGameControllerRefreshMapping( pControllerMapping ); |
| return 0; |
| } else { |
| pControllerMapping = SDL_malloc( sizeof(*pControllerMapping) ); |
| if (!pControllerMapping) { |
| SDL_free( pchName ); |
| SDL_free( pchMapping ); |
| return SDL_OutOfMemory(); |
| } |
| #if defined(SDL_JOYSTICK_DINPUT) || defined(SDL_JOYSTICK_XINPUT) |
| if ( is_xinput_mapping ) |
| { |
| s_pXInputMapping = pControllerMapping; |
| } |
| #endif |
| pControllerMapping->guid = jGUID; |
| pControllerMapping->name = pchName; |
| pControllerMapping->mapping = pchMapping; |
| pControllerMapping->next = s_pSupportedControllers; |
| s_pSupportedControllers = pControllerMapping; |
| return 1; |
| } |
| } |
| |
| /* |
| * Get the mapping string for this GUID |
| */ |
| char * |
| SDL_GameControllerMappingForGUID( SDL_JoystickGUID guid ) |
| { |
| char *pMappingString = NULL; |
| ControllerMapping_t *mapping = SDL_PrivateGetControllerMappingForGUID(&guid); |
| if (mapping) { |
| char pchGUID[33]; |
| size_t needed; |
| SDL_JoystickGetGUIDString(guid, pchGUID, sizeof(pchGUID)); |
| /* allocate enough memory for GUID + ',' + name + ',' + mapping + \0 */ |
| needed = SDL_strlen(pchGUID) + 1 + SDL_strlen(mapping->name) + 1 + SDL_strlen(mapping->mapping) + 1; |
| pMappingString = SDL_malloc( needed ); |
| SDL_snprintf( pMappingString, needed, "%s,%s,%s", pchGUID, mapping->name, mapping->mapping ); |
| } |
| return pMappingString; |
| } |
| |
| /* |
| * Get the mapping string for this device |
| */ |
| char * |
| SDL_GameControllerMapping( SDL_GameController * gamecontroller ) |
| { |
| return SDL_GameControllerMappingForGUID( gamecontroller->mapping.guid ); |
| } |
| |
| static void |
| SDL_GameControllerLoadHints() |
| { |
| const char *hint = SDL_GetHint(SDL_HINT_GAMECONTROLLERCONFIG); |
| if ( hint && hint[0] ) { |
| size_t nchHints = SDL_strlen( hint ); |
| char *pUserMappings = SDL_malloc( nchHints + 1 ); |
| char *pTempMappings = pUserMappings; |
| SDL_memcpy( pUserMappings, hint, nchHints ); |
| pUserMappings[nchHints] = '\0'; |
| while ( pUserMappings ) { |
| char *pchNewLine = NULL; |
| |
| pchNewLine = SDL_strchr( pUserMappings, '\n' ); |
| if ( pchNewLine ) |
| *pchNewLine = '\0'; |
| |
| SDL_GameControllerAddMapping( pUserMappings ); |
| |
| if ( pchNewLine ) |
| pUserMappings = pchNewLine + 1; |
| else |
| pUserMappings = NULL; |
| } |
| SDL_free(pTempMappings); |
| } |
| } |
| |
| /* |
| * Initialize the game controller system, mostly load our DB of controller config mappings |
| */ |
| int |
| SDL_GameControllerInit(void) |
| { |
| int i = 0; |
| const char *pMappingString = NULL; |
| s_pSupportedControllers = NULL; |
| pMappingString = s_ControllerMappings[i]; |
| while ( pMappingString ) { |
| SDL_GameControllerAddMapping( pMappingString ); |
| |
| i++; |
| pMappingString = s_ControllerMappings[i]; |
| } |
| |
| /* load in any user supplied config */ |
| SDL_GameControllerLoadHints(); |
| |
| /* watch for joy events and fire controller ones if needed */ |
| SDL_AddEventWatch( SDL_GameControllerEventWatcher, NULL ); |
| |
| /* Send added events for controllers currently attached */ |
| for (i = 0; i < SDL_NumJoysticks(); ++i) { |
| if (SDL_IsGameController(i)) { |
| SDL_Event deviceevent; |
| deviceevent.type = SDL_CONTROLLERDEVICEADDED; |
| deviceevent.cdevice.which = i; |
| SDL_PushEvent(&deviceevent); |
| } |
| } |
| |
| return (0); |
| } |
| |
| |
| /* |
| * Get the implementation dependent name of a controller |
| */ |
| const char * |
| SDL_GameControllerNameForIndex(int device_index) |
| { |
| ControllerMapping_t *pSupportedController = SDL_PrivateGetControllerMapping(device_index); |
| if ( pSupportedController ) |
| { |
| return pSupportedController->name; |
| } |
| return NULL; |
| } |
| |
| |
| /* |
| * Return 1 if the joystick at this device index is a supported controller |
| */ |
| SDL_bool |
| SDL_IsGameController(int device_index) |
| { |
| ControllerMapping_t *pSupportedController = SDL_PrivateGetControllerMapping(device_index); |
| if ( pSupportedController ) |
| { |
| return SDL_TRUE; |
| } |
| |
| return SDL_FALSE; |
| } |
| |
| /* |
| * Open a controller for use - the index passed as an argument refers to |
| * the N'th controller on the system. This index is the value which will |
| * identify this controller in future controller events. |
| * |
| * This function returns a controller identifier, or NULL if an error occurred. |
| */ |
| SDL_GameController * |
| SDL_GameControllerOpen(int device_index) |
| { |
| SDL_GameController *gamecontroller; |
| SDL_GameController *gamecontrollerlist; |
| ControllerMapping_t *pSupportedController = NULL; |
| |
| if ((device_index < 0) || (device_index >= SDL_NumJoysticks())) { |
| SDL_SetError("There are %d joysticks available", SDL_NumJoysticks()); |
| return (NULL); |
| } |
| |
| gamecontrollerlist = SDL_gamecontrollers; |
| /* If the controller is already open, return it */ |
| while ( gamecontrollerlist ) |
| { |
| if ( SDL_SYS_GetInstanceIdOfDeviceIndex(device_index) == gamecontrollerlist->joystick->instance_id ) { |
| gamecontroller = gamecontrollerlist; |
| ++gamecontroller->ref_count; |
| return (gamecontroller); |
| } |
| gamecontrollerlist = gamecontrollerlist->next; |
| } |
| |
| /* Find a controller mapping */ |
| pSupportedController = SDL_PrivateGetControllerMapping(device_index); |
| if ( !pSupportedController ) { |
| SDL_SetError("Couldn't find mapping for device (%d)", device_index ); |
| return (NULL); |
| } |
| |
| /* Create and initialize the joystick */ |
| gamecontroller = (SDL_GameController *) SDL_malloc((sizeof *gamecontroller)); |
| if (gamecontroller == NULL) { |
| SDL_OutOfMemory(); |
| return NULL; |
| } |
| |
| SDL_memset(gamecontroller, 0, (sizeof *gamecontroller)); |
| gamecontroller->joystick = SDL_JoystickOpen(device_index); |
| if ( !gamecontroller->joystick ) { |
| SDL_free(gamecontroller); |
| return NULL; |
| } |
| |
| SDL_PrivateLoadButtonMapping( &gamecontroller->mapping, pSupportedController->guid, pSupportedController->name, pSupportedController->mapping ); |
| |
| /* Add joystick to list */ |
| ++gamecontroller->ref_count; |
| /* Link the joystick in the list */ |
| gamecontroller->next = SDL_gamecontrollers; |
| SDL_gamecontrollers = gamecontroller; |
| |
| SDL_SYS_JoystickUpdate( gamecontroller->joystick ); |
| |
| return (gamecontroller); |
| } |
| |
| /* |
| * Manually pump for controller updates. |
| */ |
| void |
| SDL_GameControllerUpdate(void) |
| { |
| /* Just for API completeness; the joystick API does all the work. */ |
| SDL_JoystickUpdate(); |
| } |
| |
| |
| /* |
| * Get the current state of an axis control on a controller |
| */ |
| Sint16 |
| SDL_GameControllerGetAxis(SDL_GameController * gamecontroller, SDL_GameControllerAxis axis) |
| { |
| if ( !gamecontroller ) |
| return 0; |
| |
| if (gamecontroller->mapping.axes[axis] >= 0 ) |
| { |
| Sint16 value = ( SDL_JoystickGetAxis( gamecontroller->joystick, gamecontroller->mapping.axes[axis]) ); |
| switch (axis) |
| { |
| case SDL_CONTROLLER_AXIS_TRIGGERLEFT: |
| case SDL_CONTROLLER_AXIS_TRIGGERRIGHT: |
| /* Shift it to be 0 - 32767. */ |
| value = value / 2 + 16384; |
| default: |
| break; |
| } |
| return value; |
| } |
| else if (gamecontroller->mapping.buttonasaxis[axis] >= 0 ) |
| { |
| Uint8 value; |
| value = SDL_JoystickGetButton( gamecontroller->joystick, gamecontroller->mapping.buttonasaxis[axis] ); |
| if ( value > 0 ) |
| return 32767; |
| return 0; |
| } |
| return 0; |
| } |
| |
| |
| /* |
| * Get the current state of a button on a controller |
| */ |
| Uint8 |
| SDL_GameControllerGetButton(SDL_GameController * gamecontroller, SDL_GameControllerButton button) |
| { |
| if ( !gamecontroller ) |
| return 0; |
| |
| if ( gamecontroller->mapping.buttons[button] >= 0 ) |
| { |
| return ( SDL_JoystickGetButton( gamecontroller->joystick, gamecontroller->mapping.buttons[button] ) ); |
| } |
| else if ( gamecontroller->mapping.axesasbutton[button] >= 0 ) |
| { |
| Sint16 value; |
| value = SDL_JoystickGetAxis( gamecontroller->joystick, gamecontroller->mapping.axesasbutton[button] ); |
| if ( ABS(value) > 32768/2 ) |
| return 1; |
| return 0; |
| } |
| else if ( gamecontroller->mapping.hatasbutton[button].hat >= 0 ) |
| { |
| Uint8 value; |
| value = SDL_JoystickGetHat( gamecontroller->joystick, gamecontroller->mapping.hatasbutton[button].hat ); |
| |
| if ( value & gamecontroller->mapping.hatasbutton[button].mask ) |
| return 1; |
| return 0; |
| } |
| |
| return 0; |
| } |
| |
| /* |
| * Return if the joystick in question is currently attached to the system, |
| * \return 0 if not plugged in, 1 if still present. |
| */ |
| SDL_bool |
| SDL_GameControllerGetAttached( SDL_GameController * gamecontroller ) |
| { |
| if ( !gamecontroller ) |
| return SDL_FALSE; |
| |
| return SDL_JoystickGetAttached(gamecontroller->joystick); |
| } |
| |
| |
| /* |
| * Get the number of multi-dimensional axis controls on a joystick |
| */ |
| const char * |
| SDL_GameControllerName(SDL_GameController * gamecontroller) |
| { |
| if ( !gamecontroller ) |
| return NULL; |
| |
| return (gamecontroller->mapping.name); |
| } |
| |
| |
| /* |
| * Get the joystick for this controller |
| */ |
| SDL_Joystick *SDL_GameControllerGetJoystick(SDL_GameController * gamecontroller) |
| { |
| if ( !gamecontroller ) |
| return NULL; |
| |
| return gamecontroller->joystick; |
| } |
| |
| /** |
| * Get the SDL joystick layer binding for this controller axis mapping |
| */ |
| SDL_GameControllerButtonBind SDL_GameControllerGetBindForAxis(SDL_GameController * gamecontroller, SDL_GameControllerAxis axis) |
| { |
| SDL_GameControllerButtonBind bind; |
| SDL_memset( &bind, 0x0, sizeof(bind) ); |
| |
| if ( !gamecontroller || axis == SDL_CONTROLLER_AXIS_INVALID ) |
| return bind; |
| |
| if (gamecontroller->mapping.axes[axis] >= 0 ) |
| { |
| bind.bindType = SDL_CONTROLLER_BINDTYPE_AXIS; |
| bind.value.button = gamecontroller->mapping.axes[axis]; |
| } |
| else if (gamecontroller->mapping.buttonasaxis[axis] >= 0 ) |
| { |
| bind.bindType = SDL_CONTROLLER_BINDTYPE_BUTTON; |
| bind.value.button = gamecontroller->mapping.buttonasaxis[axis]; |
| } |
| |
| return bind; |
| } |
| |
| |
| /** |
| * Get the SDL joystick layer binding for this controller button mapping |
| */ |
| SDL_GameControllerButtonBind SDL_GameControllerGetBindForButton(SDL_GameController * gamecontroller, SDL_GameControllerButton button) |
| { |
| SDL_GameControllerButtonBind bind; |
| SDL_memset( &bind, 0x0, sizeof(bind) ); |
| |
| if ( !gamecontroller || button == SDL_CONTROLLER_BUTTON_INVALID ) |
| return bind; |
| |
| if ( gamecontroller->mapping.buttons[button] >= 0 ) |
| { |
| bind.bindType = SDL_CONTROLLER_BINDTYPE_BUTTON; |
| bind.value.button = gamecontroller->mapping.buttons[button]; |
| } |
| else if ( gamecontroller->mapping.axesasbutton[button] >= 0 ) |
| { |
| bind.bindType = SDL_CONTROLLER_BINDTYPE_AXIS; |
| bind.value.axis = gamecontroller->mapping.axesasbutton[button]; |
| } |
| else if ( gamecontroller->mapping.hatasbutton[button].hat >= 0 ) |
| { |
| bind.bindType = SDL_CONTROLLER_BINDTYPE_HAT; |
| bind.value.hat.hat = gamecontroller->mapping.hatasbutton[button].hat; |
| bind.value.hat.hat_mask = gamecontroller->mapping.hatasbutton[button].mask; |
| } |
| |
| return bind; |
| } |
| |
| |
| /* |
| * Close a joystick previously opened with SDL_JoystickOpen() |
| */ |
| void |
| SDL_GameControllerClose(SDL_GameController * gamecontroller) |
| { |
| SDL_GameController *gamecontrollerlist, *gamecontrollerlistprev; |
| |
| if ( !gamecontroller ) |
| return; |
| |
| /* First decrement ref count */ |
| if (--gamecontroller->ref_count > 0) { |
| return; |
| } |
| |
| SDL_JoystickClose( gamecontroller->joystick ); |
| |
| gamecontrollerlist = SDL_gamecontrollers; |
| gamecontrollerlistprev = NULL; |
| while ( gamecontrollerlist ) |
| { |
| if (gamecontroller == gamecontrollerlist) |
| { |
| if ( gamecontrollerlistprev ) |
| { |
| /* unlink this entry */ |
| gamecontrollerlistprev->next = gamecontrollerlist->next; |
| } |
| else |
| { |
| SDL_gamecontrollers = gamecontroller->next; |
| } |
| |
| break; |
| } |
| gamecontrollerlistprev = gamecontrollerlist; |
| gamecontrollerlist = gamecontrollerlist->next; |
| } |
| |
| SDL_free(gamecontroller); |
| } |
| |
| |
| /* |
| * Quit the controller subsystem |
| */ |
| void |
| SDL_GameControllerQuit(void) |
| { |
| ControllerMapping_t *pControllerMap; |
| while ( SDL_gamecontrollers ) |
| { |
| SDL_gamecontrollers->ref_count = 1; |
| SDL_GameControllerClose(SDL_gamecontrollers); |
| } |
| |
| while ( s_pSupportedControllers ) |
| { |
| pControllerMap = s_pSupportedControllers; |
| s_pSupportedControllers = s_pSupportedControllers->next; |
| SDL_free( pControllerMap->name ); |
| SDL_free( pControllerMap->mapping ); |
| SDL_free( pControllerMap ); |
| } |
| |
| SDL_DelEventWatch( SDL_GameControllerEventWatcher, NULL ); |
| |
| } |
| |
| /* |
| * Event filter to transform joystick events into appropriate game controller ones |
| */ |
| int |
| SDL_PrivateGameControllerAxis(SDL_GameController * gamecontroller, SDL_GameControllerAxis axis, Sint16 value) |
| { |
| int posted; |
| |
| /* translate the event, if desired */ |
| posted = 0; |
| #if !SDL_EVENTS_DISABLED |
| if (SDL_GetEventState(SDL_CONTROLLERAXISMOTION) == SDL_ENABLE) { |
| SDL_Event event; |
| event.type = SDL_CONTROLLERAXISMOTION; |
| event.caxis.which = gamecontroller->joystick->instance_id; |
| event.caxis.axis = axis; |
| event.caxis.value = value; |
| posted = SDL_PushEvent(&event) == 1; |
| } |
| #endif /* !SDL_EVENTS_DISABLED */ |
| return (posted); |
| } |
| |
| |
| /* |
| * Event filter to transform joystick events into appropriate game controller ones |
| */ |
| int |
| SDL_PrivateGameControllerButton(SDL_GameController * gamecontroller, SDL_GameControllerButton button, Uint8 state) |
| { |
| int posted; |
| #if !SDL_EVENTS_DISABLED |
| SDL_Event event; |
| |
| if ( button == SDL_CONTROLLER_BUTTON_INVALID ) |
| return (0); |
| |
| switch (state) { |
| case SDL_PRESSED: |
| event.type = SDL_CONTROLLERBUTTONDOWN; |
| break; |
| case SDL_RELEASED: |
| event.type = SDL_CONTROLLERBUTTONUP; |
| break; |
| default: |
| /* Invalid state -- bail */ |
| return (0); |
| } |
| #endif /* !SDL_EVENTS_DISABLED */ |
| |
| /* translate the event, if desired */ |
| posted = 0; |
| #if !SDL_EVENTS_DISABLED |
| if (SDL_GetEventState(event.type) == SDL_ENABLE) { |
| event.cbutton.which = gamecontroller->joystick->instance_id; |
| event.cbutton.button = button; |
| event.cbutton.state = state; |
| posted = SDL_PushEvent(&event) == 1; |
| } |
| #endif /* !SDL_EVENTS_DISABLED */ |
| return (posted); |
| } |
| |
| /* |
| * Turn off controller events |
| */ |
| int |
| SDL_GameControllerEventState(int state) |
| { |
| #if SDL_EVENTS_DISABLED |
| return SDL_IGNORE; |
| #else |
| const Uint32 event_list[] = { |
| SDL_CONTROLLERAXISMOTION, SDL_CONTROLLERBUTTONDOWN, SDL_CONTROLLERBUTTONUP, |
| SDL_CONTROLLERDEVICEADDED, SDL_CONTROLLERDEVICEREMOVED, SDL_CONTROLLERDEVICEREMAPPED, |
| }; |
| unsigned int i; |
| |
| switch (state) { |
| case SDL_QUERY: |
| state = SDL_IGNORE; |
| for (i = 0; i < SDL_arraysize(event_list); ++i) { |
| state = SDL_EventState(event_list[i], SDL_QUERY); |
| if (state == SDL_ENABLE) { |
| break; |
| } |
| } |
| break; |
| default: |
| for (i = 0; i < SDL_arraysize(event_list); ++i) { |
| SDL_EventState(event_list[i], state); |
| } |
| break; |
| } |
| return (state); |
| #endif /* SDL_EVENTS_DISABLED */ |
| } |
| |
| /* vi: set ts=4 sw=4 expandtab: */ |