2 Simple DirectMedia Layer
3 Copyright (C) 1997-2016 Sam Lantinga <slouken@libsdl.org>
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.
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:
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.
21 #include "../SDL_internal.h"
23 /* This is the game controller API for Simple DirectMedia Layer */
25 #include "SDL_events.h"
26 #include "SDL_assert.h"
27 #include "SDL_sysjoystick.h"
28 #include "SDL_hints.h"
29 #include "SDL_gamecontrollerdb.h"
31 #if !SDL_EVENTS_DISABLED
32 #include "../events/SDL_events_c.h"
34 #define ABS(_x) ((_x) < 0 ? -(_x) : (_x))
36 #define SDL_CONTROLLER_PLATFORM_FIELD "platform:"
38 /* a list of currently opened game controllers */
39 static SDL_GameController *SDL_gamecontrollers = NULL;
41 /* keep track of the hat and mask value that transforms this hat movement into a button/axis press */
42 struct _SDL_HatMapping
48 #define k_nMaxReverseEntries 20
51 * We are encoding the "HAT" as 0xhm. where h == hat ID and m == mask
52 * MAX 4 hats supported
54 #define k_nMaxHatEntries 0x3f + 1
56 /* our in memory mapping db between joystick objects and controller mappings */
57 struct _SDL_ControllerMapping
59 SDL_JoystickGUID guid;
62 /* mapping of axis/button id to controller version */
63 int axes[SDL_CONTROLLER_AXIS_MAX];
64 int buttonasaxis[SDL_CONTROLLER_AXIS_MAX];
66 int buttons[SDL_CONTROLLER_BUTTON_MAX];
67 int axesasbutton[SDL_CONTROLLER_BUTTON_MAX];
68 struct _SDL_HatMapping hatasbutton[SDL_CONTROLLER_BUTTON_MAX];
70 /* reverse mapping, joystick indices to buttons */
71 SDL_GameControllerAxis raxes[k_nMaxReverseEntries];
72 SDL_GameControllerAxis rbuttonasaxis[k_nMaxReverseEntries];
74 SDL_GameControllerButton rbuttons[k_nMaxReverseEntries];
75 SDL_GameControllerButton raxesasbutton[k_nMaxReverseEntries];
76 SDL_GameControllerButton rhatasbutton[k_nMaxHatEntries];
81 /* our hard coded list of mapping support */
82 typedef struct _ControllerMapping_t
84 SDL_JoystickGUID guid;
87 struct _ControllerMapping_t *next;
88 } ControllerMapping_t;
90 static ControllerMapping_t *s_pSupportedControllers = NULL;
91 static ControllerMapping_t *s_pXInputMapping = NULL;
92 static ControllerMapping_t *s_pEmscriptenMapping = NULL;
94 /* The SDL game controller structure */
95 struct _SDL_GameController
97 SDL_Joystick *joystick; /* underlying joystick device */
99 Uint8 hatState[4]; /* the current hat state for this controller */
100 struct _SDL_ControllerMapping mapping; /* the mapping object for this controller */
101 struct _SDL_GameController *next; /* pointer to next game controller we have allocated */
105 int SDL_PrivateGameControllerAxis(SDL_GameController * gamecontroller, SDL_GameControllerAxis axis, Sint16 value);
106 int SDL_PrivateGameControllerButton(SDL_GameController * gamecontroller, SDL_GameControllerButton button, Uint8 state);
109 * Event filter to fire controller events from joystick ones
111 int SDL_GameControllerEventWatcher(void *userdata, SDL_Event * event)
113 switch(event->type) {
114 case SDL_JOYAXISMOTION:
116 SDL_GameController *controllerlist;
118 if (event->jaxis.axis >= k_nMaxReverseEntries) break;
120 controllerlist = SDL_gamecontrollers;
121 while (controllerlist) {
122 if (controllerlist->joystick->instance_id == event->jaxis.which) {
123 if (controllerlist->mapping.raxes[event->jaxis.axis] >= 0) /* simple axis to axis, send it through */ {
124 SDL_GameControllerAxis axis = controllerlist->mapping.raxes[event->jaxis.axis];
125 Sint16 value = event->jaxis.value;
127 case SDL_CONTROLLER_AXIS_TRIGGERLEFT:
128 case SDL_CONTROLLER_AXIS_TRIGGERRIGHT:
129 /* Shift it to be 0 - 32767. */
130 value = value / 2 + 16384;
134 SDL_PrivateGameControllerAxis(controllerlist, axis, value);
135 } else if (controllerlist->mapping.raxesasbutton[event->jaxis.axis] >= 0) { /* simulate an axis as a button */
136 SDL_PrivateGameControllerButton(controllerlist, controllerlist->mapping.raxesasbutton[event->jaxis.axis], ABS(event->jaxis.value) > 32768/2 ? SDL_PRESSED : SDL_RELEASED);
140 controllerlist = controllerlist->next;
144 case SDL_JOYBUTTONDOWN:
145 case SDL_JOYBUTTONUP:
147 SDL_GameController *controllerlist;
149 if (event->jbutton.button >= k_nMaxReverseEntries) break;
151 controllerlist = SDL_gamecontrollers;
152 while (controllerlist) {
153 if (controllerlist->joystick->instance_id == event->jbutton.which) {
154 if (controllerlist->mapping.rbuttons[event->jbutton.button] >= 0) { /* simple button as button */
155 SDL_PrivateGameControllerButton(controllerlist, controllerlist->mapping.rbuttons[event->jbutton.button], event->jbutton.state);
156 } else if (controllerlist->mapping.rbuttonasaxis[event->jbutton.button] >= 0) { /* an button pretending to be an axis */
157 SDL_PrivateGameControllerAxis(controllerlist, controllerlist->mapping.rbuttonasaxis[event->jbutton.button], event->jbutton.state > 0 ? 32767 : 0);
161 controllerlist = controllerlist->next;
165 case SDL_JOYHATMOTION:
167 SDL_GameController *controllerlist;
169 if (event->jhat.hat >= 4) break;
171 controllerlist = SDL_gamecontrollers;
172 while (controllerlist) {
173 if (controllerlist->joystick->instance_id == event->jhat.which) {
174 Uint8 bSame = controllerlist->hatState[event->jhat.hat] & event->jhat.value;
175 /* Get list of removed bits (button release) */
176 Uint8 bChanged = controllerlist->hatState[event->jhat.hat] ^ bSame;
177 /* the hat idx in the high nibble */
178 int bHighHat = event->jhat.hat << 4;
180 if (bChanged & SDL_HAT_DOWN)
181 SDL_PrivateGameControllerButton(controllerlist, controllerlist->mapping.rhatasbutton[bHighHat | SDL_HAT_DOWN], SDL_RELEASED);
182 if (bChanged & SDL_HAT_UP)
183 SDL_PrivateGameControllerButton(controllerlist, controllerlist->mapping.rhatasbutton[bHighHat | SDL_HAT_UP], SDL_RELEASED);
184 if (bChanged & SDL_HAT_LEFT)
185 SDL_PrivateGameControllerButton(controllerlist, controllerlist->mapping.rhatasbutton[bHighHat | SDL_HAT_LEFT], SDL_RELEASED);
186 if (bChanged & SDL_HAT_RIGHT)
187 SDL_PrivateGameControllerButton(controllerlist, controllerlist->mapping.rhatasbutton[bHighHat | SDL_HAT_RIGHT], SDL_RELEASED);
189 /* Get list of added bits (button press) */
190 bChanged = event->jhat.value ^ bSame;
192 if (bChanged & SDL_HAT_DOWN)
193 SDL_PrivateGameControllerButton(controllerlist, controllerlist->mapping.rhatasbutton[bHighHat | SDL_HAT_DOWN], SDL_PRESSED);
194 if (bChanged & SDL_HAT_UP)
195 SDL_PrivateGameControllerButton(controllerlist, controllerlist->mapping.rhatasbutton[bHighHat | SDL_HAT_UP], SDL_PRESSED);
196 if (bChanged & SDL_HAT_LEFT)
197 SDL_PrivateGameControllerButton(controllerlist, controllerlist->mapping.rhatasbutton[bHighHat | SDL_HAT_LEFT], SDL_PRESSED);
198 if (bChanged & SDL_HAT_RIGHT)
199 SDL_PrivateGameControllerButton(controllerlist, controllerlist->mapping.rhatasbutton[bHighHat | SDL_HAT_RIGHT], SDL_PRESSED);
201 /* update our state cache */
202 controllerlist->hatState[event->jhat.hat] = event->jhat.value;
206 controllerlist = controllerlist->next;
210 case SDL_JOYDEVICEADDED:
212 if (SDL_IsGameController(event->jdevice.which)) {
213 SDL_Event deviceevent;
214 deviceevent.type = SDL_CONTROLLERDEVICEADDED;
215 deviceevent.cdevice.which = event->jdevice.which;
216 SDL_PushEvent(&deviceevent);
220 case SDL_JOYDEVICEREMOVED:
222 SDL_GameController *controllerlist = SDL_gamecontrollers;
223 while (controllerlist) {
224 if (controllerlist->joystick->instance_id == event->jdevice.which) {
225 SDL_Event deviceevent;
226 deviceevent.type = SDL_CONTROLLERDEVICEREMOVED;
227 deviceevent.cdevice.which = event->jdevice.which;
228 SDL_PushEvent(&deviceevent);
231 controllerlist = controllerlist->next;
243 * Helper function to scan the mappings database for a controller with the specified GUID
245 ControllerMapping_t *SDL_PrivateGetControllerMappingForGUID(SDL_JoystickGUID *guid)
247 ControllerMapping_t *pSupportedController = s_pSupportedControllers;
248 while (pSupportedController) {
249 if (SDL_memcmp(guid, &pSupportedController->guid, sizeof(*guid)) == 0) {
250 return pSupportedController;
252 pSupportedController = pSupportedController->next;
257 static const char* map_StringForControllerAxis[] = {
268 * convert a string to its enum equivalent
270 SDL_GameControllerAxis SDL_GameControllerGetAxisFromString(const char *pchString)
273 if (!pchString || !pchString[0])
274 return SDL_CONTROLLER_AXIS_INVALID;
276 for (entry = 0; map_StringForControllerAxis[entry]; ++entry) {
277 if (!SDL_strcasecmp(pchString, map_StringForControllerAxis[entry]))
280 return SDL_CONTROLLER_AXIS_INVALID;
284 * convert an enum to its string equivalent
286 const char* SDL_GameControllerGetStringForAxis(SDL_GameControllerAxis axis)
288 if (axis > SDL_CONTROLLER_AXIS_INVALID && axis < SDL_CONTROLLER_AXIS_MAX) {
289 return map_StringForControllerAxis[axis];
294 static const char* map_StringForControllerButton[] = {
314 * convert a string to its enum equivalent
316 SDL_GameControllerButton SDL_GameControllerGetButtonFromString(const char *pchString)
319 if (!pchString || !pchString[0])
320 return SDL_CONTROLLER_BUTTON_INVALID;
322 for (entry = 0; map_StringForControllerButton[entry]; ++entry) {
323 if (SDL_strcasecmp(pchString, map_StringForControllerButton[entry]) == 0)
326 return SDL_CONTROLLER_BUTTON_INVALID;
330 * convert an enum to its string equivalent
332 const char* SDL_GameControllerGetStringForButton(SDL_GameControllerButton axis)
334 if (axis > SDL_CONTROLLER_BUTTON_INVALID && axis < SDL_CONTROLLER_BUTTON_MAX) {
335 return map_StringForControllerButton[axis];
341 * given a controller button name and a joystick name update our mapping structure with it
343 void SDL_PrivateGameControllerParseButton(const char *szGameButton, const char *szJoystickButton, struct _SDL_ControllerMapping *pMapping)
346 SDL_GameControllerButton button;
347 SDL_GameControllerAxis axis;
348 button = SDL_GameControllerGetButtonFromString(szGameButton);
349 axis = SDL_GameControllerGetAxisFromString(szGameButton);
350 iSDLButton = SDL_atoi(&szJoystickButton[1]);
352 if (szJoystickButton[0] == 'a') {
353 if (iSDLButton >= k_nMaxReverseEntries) {
354 SDL_SetError("Axis index too large: %d", iSDLButton);
357 if (axis != SDL_CONTROLLER_AXIS_INVALID) {
358 pMapping->axes[ axis ] = iSDLButton;
359 pMapping->raxes[ iSDLButton ] = axis;
360 } else if (button != SDL_CONTROLLER_BUTTON_INVALID) {
361 pMapping->axesasbutton[ button ] = iSDLButton;
362 pMapping->raxesasbutton[ iSDLButton ] = button;
364 SDL_assert(!"How did we get here?");
367 } else if (szJoystickButton[0] == 'b') {
368 if (iSDLButton >= k_nMaxReverseEntries) {
369 SDL_SetError("Button index too large: %d", iSDLButton);
372 if (button != SDL_CONTROLLER_BUTTON_INVALID) {
373 pMapping->buttons[ button ] = iSDLButton;
374 pMapping->rbuttons[ iSDLButton ] = button;
375 } else if (axis != SDL_CONTROLLER_AXIS_INVALID) {
376 pMapping->buttonasaxis[ axis ] = iSDLButton;
377 pMapping->rbuttonasaxis[ iSDLButton ] = axis;
379 SDL_assert(!"How did we get here?");
381 } else if (szJoystickButton[0] == 'h') {
382 int hat = SDL_atoi(&szJoystickButton[1]);
383 int mask = SDL_atoi(&szJoystickButton[3]);
385 SDL_SetError("Hat index too large: %d", iSDLButton);
388 if (button != SDL_CONTROLLER_BUTTON_INVALID) {
390 pMapping->hatasbutton[ button ].hat = hat;
391 pMapping->hatasbutton[ button ].mask = mask;
392 ridx = (hat << 4) | mask;
393 pMapping->rhatasbutton[ ridx ] = button;
394 } else if (axis != SDL_CONTROLLER_AXIS_INVALID) {
395 SDL_assert(!"Support hat as axis");
397 SDL_assert(!"How did we get here?");
404 * given a controller mapping string update our mapping object
407 SDL_PrivateGameControllerParseControllerConfigString(struct _SDL_ControllerMapping *pMapping, const char *pchString)
409 char szGameButton[20];
410 char szJoystickButton[20];
411 SDL_bool bGameButton = SDL_TRUE;
413 const char *pchPos = pchString;
415 SDL_memset(szGameButton, 0x0, sizeof(szGameButton));
416 SDL_memset(szJoystickButton, 0x0, sizeof(szJoystickButton));
418 while (pchPos && *pchPos) {
419 if (*pchPos == ':') {
421 bGameButton = SDL_FALSE;
422 } else if (*pchPos == ' ') {
424 } else if (*pchPos == ',') {
426 bGameButton = SDL_TRUE;
427 SDL_PrivateGameControllerParseButton(szGameButton, szJoystickButton, pMapping);
428 SDL_memset(szGameButton, 0x0, sizeof(szGameButton));
429 SDL_memset(szJoystickButton, 0x0, sizeof(szJoystickButton));
431 } else if (bGameButton) {
432 if (i >= sizeof(szGameButton)) {
433 SDL_SetError("Button name too large: %s", szGameButton);
436 szGameButton[i] = *pchPos;
439 if (i >= sizeof(szJoystickButton)) {
440 SDL_SetError("Joystick button name too large: %s", szJoystickButton);
443 szJoystickButton[i] = *pchPos;
449 SDL_PrivateGameControllerParseButton(szGameButton, szJoystickButton, pMapping);
454 * Make a new button mapping struct
456 void SDL_PrivateLoadButtonMapping(struct _SDL_ControllerMapping *pMapping, SDL_JoystickGUID guid, const char *pchName, const char *pchMapping)
460 pMapping->guid = guid;
461 pMapping->name = pchName;
463 /* set all the button mappings to non defaults */
464 for (j = 0; j < SDL_CONTROLLER_AXIS_MAX; j++) {
465 pMapping->axes[j] = -1;
466 pMapping->buttonasaxis[j] = -1;
468 for (j = 0; j < SDL_CONTROLLER_BUTTON_MAX; j++) {
469 pMapping->buttons[j] = -1;
470 pMapping->axesasbutton[j] = -1;
471 pMapping->hatasbutton[j].hat = -1;
474 for (j = 0; j < k_nMaxReverseEntries; j++) {
475 pMapping->raxes[j] = SDL_CONTROLLER_AXIS_INVALID;
476 pMapping->rbuttonasaxis[j] = SDL_CONTROLLER_AXIS_INVALID;
477 pMapping->rbuttons[j] = SDL_CONTROLLER_BUTTON_INVALID;
478 pMapping->raxesasbutton[j] = SDL_CONTROLLER_BUTTON_INVALID;
481 for (j = 0; j < k_nMaxHatEntries; j++) {
482 pMapping->rhatasbutton[j] = SDL_CONTROLLER_BUTTON_INVALID;
485 SDL_PrivateGameControllerParseControllerConfigString(pMapping, pchMapping);
490 * grab the guid string from a mapping string
492 char *SDL_PrivateGetControllerGUIDFromMappingString(const char *pMapping)
494 const char *pFirstComma = SDL_strchr(pMapping, ',');
496 char *pchGUID = SDL_malloc(pFirstComma - pMapping + 1);
501 SDL_memcpy(pchGUID, pMapping, pFirstComma - pMapping);
502 pchGUID[ pFirstComma - pMapping ] = 0;
510 * grab the name string from a mapping string
512 char *SDL_PrivateGetControllerNameFromMappingString(const char *pMapping)
514 const char *pFirstComma, *pSecondComma;
517 pFirstComma = SDL_strchr(pMapping, ',');
521 pSecondComma = SDL_strchr(pFirstComma + 1, ',');
525 pchName = SDL_malloc(pSecondComma - pFirstComma);
530 SDL_memcpy(pchName, pFirstComma + 1, pSecondComma - pFirstComma);
531 pchName[ pSecondComma - pFirstComma - 1 ] = 0;
537 * grab the button mapping string from a mapping string
539 char *SDL_PrivateGetControllerMappingFromMappingString(const char *pMapping)
541 const char *pFirstComma, *pSecondComma;
543 pFirstComma = SDL_strchr(pMapping, ',');
547 pSecondComma = SDL_strchr(pFirstComma + 1, ',');
551 return SDL_strdup(pSecondComma + 1); /* mapping is everything after the 3rd comma */
555 * Helper function to refresh a mapping
557 void SDL_PrivateGameControllerRefreshMapping(ControllerMapping_t *pControllerMapping)
559 SDL_GameController *gamecontrollerlist = SDL_gamecontrollers;
560 while (gamecontrollerlist) {
561 if (!SDL_memcmp(&gamecontrollerlist->mapping.guid, &pControllerMapping->guid, sizeof(pControllerMapping->guid))) {
563 event.type = SDL_CONTROLLERDEVICEREMAPPED;
564 event.cdevice.which = gamecontrollerlist->joystick->instance_id;
565 SDL_PushEvent(&event);
567 /* Not really threadsafe. Should this lock access within SDL_GameControllerEventWatcher? */
568 SDL_PrivateLoadButtonMapping(&gamecontrollerlist->mapping, pControllerMapping->guid, pControllerMapping->name, pControllerMapping->mapping);
571 gamecontrollerlist = gamecontrollerlist->next;
576 * Helper function to add a mapping for a guid
578 static ControllerMapping_t *
579 SDL_PrivateAddMappingForGUID(SDL_JoystickGUID jGUID, const char *mappingString, SDL_bool *existing)
583 ControllerMapping_t *pControllerMapping;
585 pchName = SDL_PrivateGetControllerNameFromMappingString(mappingString);
587 SDL_SetError("Couldn't parse name from %s", mappingString);
591 pchMapping = SDL_PrivateGetControllerMappingFromMappingString(mappingString);
594 SDL_SetError("Couldn't parse %s", mappingString);
598 pControllerMapping = SDL_PrivateGetControllerMappingForGUID(&jGUID);
599 if (pControllerMapping) {
600 /* Update existing mapping */
601 SDL_free(pControllerMapping->name);
602 pControllerMapping->name = pchName;
603 SDL_free(pControllerMapping->mapping);
604 pControllerMapping->mapping = pchMapping;
605 /* refresh open controllers */
606 SDL_PrivateGameControllerRefreshMapping(pControllerMapping);
607 *existing = SDL_TRUE;
609 pControllerMapping = SDL_malloc(sizeof(*pControllerMapping));
610 if (!pControllerMapping) {
612 SDL_free(pchMapping);
616 pControllerMapping->guid = jGUID;
617 pControllerMapping->name = pchName;
618 pControllerMapping->mapping = pchMapping;
619 pControllerMapping->next = s_pSupportedControllers;
620 s_pSupportedControllers = pControllerMapping;
621 *existing = SDL_FALSE;
623 return pControllerMapping;
627 * Helper function to determine pre-calculated offset to certain joystick mappings
629 ControllerMapping_t *SDL_PrivateGetControllerMapping(int device_index)
631 SDL_JoystickGUID jGUID = SDL_JoystickGetDeviceGUID(device_index);
632 ControllerMapping_t *mapping;
634 mapping = SDL_PrivateGetControllerMappingForGUID(&jGUID);
635 #if SDL_JOYSTICK_XINPUT
636 if (!mapping && SDL_SYS_IsXInputGamepad_DeviceIndex(device_index)) {
637 mapping = s_pXInputMapping;
640 #if defined(SDL_JOYSTICK_EMSCRIPTEN)
641 if (!mapping && s_pEmscriptenMapping) {
642 mapping = s_pEmscriptenMapping;
647 const char *name = SDL_JoystickNameForIndex(device_index);
649 if (SDL_strstr(name, "Xbox 360 Wireless Receiver")) {
650 /* The Linux driver xpad.c maps the wireless dpad to buttons */
652 mapping = SDL_PrivateAddMappingForGUID(jGUID,
653 "none,X360 Wireless Controller,a:b0,b:b1,back:b6,dpdown:b14,dpleft:b11,dpright:b12,dpup:b13,guide:b8,leftshoulder:b4,leftstick:b9,lefttrigger:a2,leftx:a0,lefty:a1,rightshoulder:b5,rightstick:b10,righttrigger:a5,rightx:a3,righty:a4,start:b7,x:b2,y:b3,",
658 #endif /* __LINUX__ */
661 const char *name = SDL_JoystickNameForIndex(device_index);
663 if (SDL_strstr(name, "Xbox") || SDL_strstr(name, "X-Box")) {
664 mapping = s_pXInputMapping;
672 * Add or update an entry into the Mappings Database
675 SDL_GameControllerAddMappingsFromRW(SDL_RWops * rw, int freerw)
677 const char *platform = SDL_GetPlatform();
679 char *buf, *line, *line_end, *tmp, *comma, line_platform[64];
680 size_t db_size, platform_len;
683 return SDL_SetError("Invalid RWops");
685 db_size = (size_t)SDL_RWsize(rw);
687 buf = (char *)SDL_malloc(db_size + 1);
692 return SDL_SetError("Could not allocate space to read DB into memory");
695 if (SDL_RWread(rw, buf, db_size, 1) != 1) {
700 return SDL_SetError("Could not read DB");
710 while (line < buf + db_size) {
711 line_end = SDL_strchr(line, '\n');
712 if (line_end != NULL) {
715 line_end = buf + db_size;
718 /* Extract and verify the platform */
719 tmp = SDL_strstr(line, SDL_CONTROLLER_PLATFORM_FIELD);
721 tmp += SDL_strlen(SDL_CONTROLLER_PLATFORM_FIELD);
722 comma = SDL_strchr(tmp, ',');
724 platform_len = comma - tmp + 1;
725 if (platform_len + 1 < SDL_arraysize(line_platform)) {
726 SDL_strlcpy(line_platform, tmp, platform_len);
727 if (SDL_strncasecmp(line_platform, platform, platform_len) == 0 &&
728 SDL_GameControllerAddMapping(line) > 0) {
743 * Add or update an entry into the Mappings Database
746 SDL_GameControllerAddMapping(const char *mappingString)
749 SDL_JoystickGUID jGUID;
750 SDL_bool is_xinput_mapping = SDL_FALSE;
751 SDL_bool is_emscripten_mapping = SDL_FALSE;
752 SDL_bool existing = SDL_FALSE;
753 ControllerMapping_t *pControllerMapping;
755 if (!mappingString) {
756 return SDL_InvalidParamError("mappingString");
759 pchGUID = SDL_PrivateGetControllerGUIDFromMappingString(mappingString);
761 return SDL_SetError("Couldn't parse GUID from %s", mappingString);
763 if (!SDL_strcasecmp(pchGUID, "xinput")) {
764 is_xinput_mapping = SDL_TRUE;
766 if (!SDL_strcasecmp(pchGUID, "emscripten")) {
767 is_emscripten_mapping = SDL_TRUE;
769 jGUID = SDL_JoystickGetGUIDFromString(pchGUID);
772 pControllerMapping = SDL_PrivateAddMappingForGUID(jGUID, mappingString, &existing);
773 if (!pControllerMapping) {
780 if (is_xinput_mapping) {
781 s_pXInputMapping = pControllerMapping;
783 if (is_emscripten_mapping) {
784 s_pEmscriptenMapping = pControllerMapping;
791 * Get the mapping string for this GUID
794 SDL_GameControllerMappingForGUID(SDL_JoystickGUID guid)
796 char *pMappingString = NULL;
797 ControllerMapping_t *mapping = SDL_PrivateGetControllerMappingForGUID(&guid);
801 SDL_JoystickGetGUIDString(guid, pchGUID, sizeof(pchGUID));
802 /* allocate enough memory for GUID + ',' + name + ',' + mapping + \0 */
803 needed = SDL_strlen(pchGUID) + 1 + SDL_strlen(mapping->name) + 1 + SDL_strlen(mapping->mapping) + 1;
804 pMappingString = SDL_malloc(needed);
805 if (!pMappingString) {
809 SDL_snprintf(pMappingString, needed, "%s,%s,%s", pchGUID, mapping->name, mapping->mapping);
811 return pMappingString;
815 * Get the mapping string for this device
818 SDL_GameControllerMapping(SDL_GameController * gamecontroller)
820 if (!gamecontroller) {
824 return SDL_GameControllerMappingForGUID(gamecontroller->mapping.guid);
828 SDL_GameControllerLoadHints()
830 const char *hint = SDL_GetHint(SDL_HINT_GAMECONTROLLERCONFIG);
831 if (hint && hint[0]) {
832 size_t nchHints = SDL_strlen(hint);
833 char *pUserMappings = SDL_malloc(nchHints + 1);
834 char *pTempMappings = pUserMappings;
835 SDL_memcpy(pUserMappings, hint, nchHints);
836 pUserMappings[nchHints] = '\0';
837 while (pUserMappings) {
838 char *pchNewLine = NULL;
840 pchNewLine = SDL_strchr(pUserMappings, '\n');
844 SDL_GameControllerAddMapping(pUserMappings);
847 pUserMappings = pchNewLine + 1;
849 pUserMappings = NULL;
852 SDL_free(pTempMappings);
857 * Initialize the game controller system, mostly load our DB of controller config mappings
860 SDL_GameControllerInit(void)
863 const char *pMappingString = NULL;
864 s_pSupportedControllers = NULL;
865 pMappingString = s_ControllerMappings[i];
866 while (pMappingString) {
867 SDL_GameControllerAddMapping(pMappingString);
870 pMappingString = s_ControllerMappings[i];
873 /* load in any user supplied config */
874 SDL_GameControllerLoadHints();
876 /* watch for joy events and fire controller ones if needed */
877 SDL_AddEventWatch(SDL_GameControllerEventWatcher, NULL);
879 /* Send added events for controllers currently attached */
880 for (i = 0; i < SDL_NumJoysticks(); ++i) {
881 if (SDL_IsGameController(i)) {
882 SDL_Event deviceevent;
883 deviceevent.type = SDL_CONTROLLERDEVICEADDED;
884 deviceevent.cdevice.which = i;
885 SDL_PushEvent(&deviceevent);
894 * Get the implementation dependent name of a controller
897 SDL_GameControllerNameForIndex(int device_index)
899 ControllerMapping_t *pSupportedController = SDL_PrivateGetControllerMapping(device_index);
900 if (pSupportedController) {
901 return pSupportedController->name;
908 * Return 1 if the joystick at this device index is a supported controller
911 SDL_IsGameController(int device_index)
913 ControllerMapping_t *pSupportedController = SDL_PrivateGetControllerMapping(device_index);
914 if (pSupportedController) {
922 * Open a controller for use - the index passed as an argument refers to
923 * the N'th controller on the system. This index is the value which will
924 * identify this controller in future controller events.
926 * This function returns a controller identifier, or NULL if an error occurred.
929 SDL_GameControllerOpen(int device_index)
931 SDL_GameController *gamecontroller;
932 SDL_GameController *gamecontrollerlist;
933 ControllerMapping_t *pSupportedController = NULL;
935 if ((device_index < 0) || (device_index >= SDL_NumJoysticks())) {
936 SDL_SetError("There are %d joysticks available", SDL_NumJoysticks());
940 gamecontrollerlist = SDL_gamecontrollers;
941 /* If the controller is already open, return it */
942 while (gamecontrollerlist) {
943 if (SDL_SYS_GetInstanceIdOfDeviceIndex(device_index) == gamecontrollerlist->joystick->instance_id) {
944 gamecontroller = gamecontrollerlist;
945 ++gamecontroller->ref_count;
946 return (gamecontroller);
948 gamecontrollerlist = gamecontrollerlist->next;
951 /* Find a controller mapping */
952 pSupportedController = SDL_PrivateGetControllerMapping(device_index);
953 if (!pSupportedController) {
954 SDL_SetError("Couldn't find mapping for device (%d)", device_index);
958 /* Create and initialize the joystick */
959 gamecontroller = (SDL_GameController *) SDL_malloc((sizeof *gamecontroller));
960 if (gamecontroller == NULL) {
965 SDL_memset(gamecontroller, 0, (sizeof *gamecontroller));
966 gamecontroller->joystick = SDL_JoystickOpen(device_index);
967 if (!gamecontroller->joystick) {
968 SDL_free(gamecontroller);
972 SDL_PrivateLoadButtonMapping(&gamecontroller->mapping, pSupportedController->guid, pSupportedController->name, pSupportedController->mapping);
974 /* Add joystick to list */
975 ++gamecontroller->ref_count;
976 /* Link the joystick in the list */
977 gamecontroller->next = SDL_gamecontrollers;
978 SDL_gamecontrollers = gamecontroller;
980 SDL_SYS_JoystickUpdate(gamecontroller->joystick);
982 return (gamecontroller);
986 * Manually pump for controller updates.
989 SDL_GameControllerUpdate(void)
991 /* Just for API completeness; the joystick API does all the work. */
992 SDL_JoystickUpdate();
997 * Get the current state of an axis control on a controller
1000 SDL_GameControllerGetAxis(SDL_GameController * gamecontroller, SDL_GameControllerAxis axis)
1002 if (!gamecontroller)
1005 if (gamecontroller->mapping.axes[axis] >= 0) {
1006 Sint16 value = (SDL_JoystickGetAxis(gamecontroller->joystick, gamecontroller->mapping.axes[axis]));
1008 case SDL_CONTROLLER_AXIS_TRIGGERLEFT:
1009 case SDL_CONTROLLER_AXIS_TRIGGERRIGHT:
1010 /* Shift it to be 0 - 32767. */
1011 value = value / 2 + 16384;
1016 } else if (gamecontroller->mapping.buttonasaxis[axis] >= 0) {
1018 value = SDL_JoystickGetButton(gamecontroller->joystick, gamecontroller->mapping.buttonasaxis[axis]);
1028 * Get the current state of a button on a controller
1031 SDL_GameControllerGetButton(SDL_GameController * gamecontroller, SDL_GameControllerButton button)
1033 if (!gamecontroller)
1036 if (gamecontroller->mapping.buttons[button] >= 0) {
1037 return (SDL_JoystickGetButton(gamecontroller->joystick, gamecontroller->mapping.buttons[button]));
1038 } else if (gamecontroller->mapping.axesasbutton[button] >= 0) {
1040 value = SDL_JoystickGetAxis(gamecontroller->joystick, gamecontroller->mapping.axesasbutton[button]);
1041 if (ABS(value) > 32768/2)
1044 } else if (gamecontroller->mapping.hatasbutton[button].hat >= 0) {
1046 value = SDL_JoystickGetHat(gamecontroller->joystick, gamecontroller->mapping.hatasbutton[button].hat);
1048 if (value & gamecontroller->mapping.hatasbutton[button].mask)
1057 * Return if the joystick in question is currently attached to the system,
1058 * \return 0 if not plugged in, 1 if still present.
1061 SDL_GameControllerGetAttached(SDL_GameController * gamecontroller)
1063 if (!gamecontroller)
1066 return SDL_JoystickGetAttached(gamecontroller->joystick);
1071 SDL_GameControllerName(SDL_GameController * gamecontroller)
1073 if (!gamecontroller)
1076 return (gamecontroller->mapping.name);
1081 * Get the joystick for this controller
1083 SDL_Joystick *SDL_GameControllerGetJoystick(SDL_GameController * gamecontroller)
1085 if (!gamecontroller)
1088 return gamecontroller->joystick;
1093 * Find the SDL_GameController that owns this instance id
1095 SDL_GameController *
1096 SDL_GameControllerFromInstanceID(SDL_JoystickID joyid)
1098 SDL_GameController *gamecontroller = SDL_gamecontrollers;
1099 while (gamecontroller) {
1100 if (gamecontroller->joystick->instance_id == joyid) {
1101 return gamecontroller;
1103 gamecontroller = gamecontroller->next;
1111 * Get the SDL joystick layer binding for this controller axis mapping
1113 SDL_GameControllerButtonBind SDL_GameControllerGetBindForAxis(SDL_GameController * gamecontroller, SDL_GameControllerAxis axis)
1115 SDL_GameControllerButtonBind bind;
1116 SDL_memset(&bind, 0x0, sizeof(bind));
1118 if (!gamecontroller || axis == SDL_CONTROLLER_AXIS_INVALID)
1121 if (gamecontroller->mapping.axes[axis] >= 0) {
1122 bind.bindType = SDL_CONTROLLER_BINDTYPE_AXIS;
1123 bind.value.button = gamecontroller->mapping.axes[axis];
1124 } else if (gamecontroller->mapping.buttonasaxis[axis] >= 0) {
1125 bind.bindType = SDL_CONTROLLER_BINDTYPE_BUTTON;
1126 bind.value.button = gamecontroller->mapping.buttonasaxis[axis];
1134 * Get the SDL joystick layer binding for this controller button mapping
1136 SDL_GameControllerButtonBind SDL_GameControllerGetBindForButton(SDL_GameController * gamecontroller, SDL_GameControllerButton button)
1138 SDL_GameControllerButtonBind bind;
1139 SDL_memset(&bind, 0x0, sizeof(bind));
1141 if (!gamecontroller || button == SDL_CONTROLLER_BUTTON_INVALID)
1144 if (gamecontroller->mapping.buttons[button] >= 0) {
1145 bind.bindType = SDL_CONTROLLER_BINDTYPE_BUTTON;
1146 bind.value.button = gamecontroller->mapping.buttons[button];
1147 } else if (gamecontroller->mapping.axesasbutton[button] >= 0) {
1148 bind.bindType = SDL_CONTROLLER_BINDTYPE_AXIS;
1149 bind.value.axis = gamecontroller->mapping.axesasbutton[button];
1150 } else if (gamecontroller->mapping.hatasbutton[button].hat >= 0) {
1151 bind.bindType = SDL_CONTROLLER_BINDTYPE_HAT;
1152 bind.value.hat.hat = gamecontroller->mapping.hatasbutton[button].hat;
1153 bind.value.hat.hat_mask = gamecontroller->mapping.hatasbutton[button].mask;
1161 SDL_GameControllerClose(SDL_GameController * gamecontroller)
1163 SDL_GameController *gamecontrollerlist, *gamecontrollerlistprev;
1165 if (!gamecontroller)
1168 /* First decrement ref count */
1169 if (--gamecontroller->ref_count > 0) {
1173 SDL_JoystickClose(gamecontroller->joystick);
1175 gamecontrollerlist = SDL_gamecontrollers;
1176 gamecontrollerlistprev = NULL;
1177 while (gamecontrollerlist) {
1178 if (gamecontroller == gamecontrollerlist) {
1179 if (gamecontrollerlistprev) {
1180 /* unlink this entry */
1181 gamecontrollerlistprev->next = gamecontrollerlist->next;
1183 SDL_gamecontrollers = gamecontroller->next;
1188 gamecontrollerlistprev = gamecontrollerlist;
1189 gamecontrollerlist = gamecontrollerlist->next;
1192 SDL_free(gamecontroller);
1197 * Quit the controller subsystem
1200 SDL_GameControllerQuit(void)
1202 ControllerMapping_t *pControllerMap;
1203 while (SDL_gamecontrollers) {
1204 SDL_gamecontrollers->ref_count = 1;
1205 SDL_GameControllerClose(SDL_gamecontrollers);
1208 while (s_pSupportedControllers) {
1209 pControllerMap = s_pSupportedControllers;
1210 s_pSupportedControllers = s_pSupportedControllers->next;
1211 SDL_free(pControllerMap->name);
1212 SDL_free(pControllerMap->mapping);
1213 SDL_free(pControllerMap);
1216 SDL_DelEventWatch(SDL_GameControllerEventWatcher, NULL);
1221 * Event filter to transform joystick events into appropriate game controller ones
1224 SDL_PrivateGameControllerAxis(SDL_GameController * gamecontroller, SDL_GameControllerAxis axis, Sint16 value)
1228 /* translate the event, if desired */
1230 #if !SDL_EVENTS_DISABLED
1231 if (SDL_GetEventState(SDL_CONTROLLERAXISMOTION) == SDL_ENABLE) {
1233 event.type = SDL_CONTROLLERAXISMOTION;
1234 event.caxis.which = gamecontroller->joystick->instance_id;
1235 event.caxis.axis = axis;
1236 event.caxis.value = value;
1237 posted = SDL_PushEvent(&event) == 1;
1239 #endif /* !SDL_EVENTS_DISABLED */
1245 * Event filter to transform joystick events into appropriate game controller ones
1248 SDL_PrivateGameControllerButton(SDL_GameController * gamecontroller, SDL_GameControllerButton button, Uint8 state)
1251 #if !SDL_EVENTS_DISABLED
1254 if (button == SDL_CONTROLLER_BUTTON_INVALID)
1259 event.type = SDL_CONTROLLERBUTTONDOWN;
1262 event.type = SDL_CONTROLLERBUTTONUP;
1265 /* Invalid state -- bail */
1268 #endif /* !SDL_EVENTS_DISABLED */
1270 /* translate the event, if desired */
1272 #if !SDL_EVENTS_DISABLED
1273 if (SDL_GetEventState(event.type) == SDL_ENABLE) {
1274 event.cbutton.which = gamecontroller->joystick->instance_id;
1275 event.cbutton.button = button;
1276 event.cbutton.state = state;
1277 posted = SDL_PushEvent(&event) == 1;
1279 #endif /* !SDL_EVENTS_DISABLED */
1284 * Turn off controller events
1287 SDL_GameControllerEventState(int state)
1289 #if SDL_EVENTS_DISABLED
1292 const Uint32 event_list[] = {
1293 SDL_CONTROLLERAXISMOTION, SDL_CONTROLLERBUTTONDOWN, SDL_CONTROLLERBUTTONUP,
1294 SDL_CONTROLLERDEVICEADDED, SDL_CONTROLLERDEVICEREMOVED, SDL_CONTROLLERDEVICEREMAPPED,
1301 for (i = 0; i < SDL_arraysize(event_list); ++i) {
1302 state = SDL_EventState(event_list[i], SDL_QUERY);
1303 if (state == SDL_ENABLE) {
1309 for (i = 0; i < SDL_arraysize(event_list); ++i) {
1310 SDL_EventState(event_list[i], state);
1315 #endif /* SDL_EVENTS_DISABLED */
1318 /* vi: set ts=4 sw=4 expandtab: */