Browse Source

Split controller axes into positive and negative sides so each can be bound independently.
Using this a D-Pad can be mapped to a thumbstick and vice versa.
Also added support for inverted axes, improving trigger binding support

Sam Lantinga 8 years ago
parent
commit
6d7da0887d

+ 2 - 0
include/SDL_joystick.h

@@ -232,6 +232,8 @@ extern DECLSPEC void SDLCALL SDL_JoystickUpdate(void);
  */
 extern DECLSPEC int SDLCALL SDL_JoystickEventState(int state);
 
+#define SDL_JOYSTICK_AXIS_MAX   32767
+#define SDL_JOYSTICK_AXIS_MIN   -32768
 /**
  *  Get the current state of an axis control on a joystick.
  *

+ 392 - 270
src/joystick/SDL_gamecontroller.c

@@ -32,53 +32,46 @@
 #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
+typedef struct
 {
-    int hat;
-    Uint8 mask;
-};
+    SDL_GameControllerBindType inputType;
+    union
+    {
+        int button;
 
-/* We need 36 entries for Android (as of SDL v2.0.4) */
-#define k_nMaxReverseEntries 48
+        struct {
+            int axis;
+            int axis_min;
+            int axis_max;
+        } axis;
 
-/**
- * We are encoding the "HAT" as 0xhm. where h == hat ID and m == mask
- * MAX 4 hats supported
- */
-#define k_nMaxHatEntries 0x3f + 1
+        struct {
+            int hat;
+            int hat_mask;
+        } hat;
 
-/* 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];
+    } input;
 
-    /* reverse mapping, joystick indices to buttons */
-    SDL_GameControllerAxis raxes[k_nMaxReverseEntries];
-    SDL_GameControllerAxis rbuttonasaxis[k_nMaxReverseEntries];
+    SDL_GameControllerBindType outputType;
+    union
+    {
+        SDL_GameControllerButton button;
 
-    SDL_GameControllerButton rbuttons[k_nMaxReverseEntries];
-    SDL_GameControllerButton raxesasbutton[k_nMaxReverseEntries];
-    SDL_GameControllerButton rhatasbutton[k_nMaxHatEntries];
+        struct {
+            SDL_GameControllerAxis axis;
+            int axis_min;
+            int axis_max;
+        } axis;
 
-};
+    } output;
 
+} SDL_ExtendedGameControllerBind;
 
 /* our hard coded list of mapping support */
 typedef enum
@@ -107,14 +100,20 @@ 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 */
+
+    SDL_JoystickGUID guid;
+    const char *name;
+    int num_bindings;
+    SDL_ExtendedGameControllerBind *bindings;
+    SDL_ExtendedGameControllerBind **last_match_axis;
+    Uint8 *last_hat_mask;
+
     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);
+static int SDL_PrivateGameControllerAxis(SDL_GameController * gamecontroller, SDL_GameControllerAxis axis, Sint16 value);
+static int SDL_PrivateGameControllerButton(SDL_GameController * gamecontroller, SDL_GameControllerButton button, Uint8 state);
 
 /*
  * If there is an existing add event in the queue, it needs to be modified
@@ -145,6 +144,124 @@ static void UpdateEventsForDeviceRemoval()
     SDL_stack_free(events);
 }
 
+static SDL_bool HasSameOutput(SDL_ExtendedGameControllerBind *a, SDL_ExtendedGameControllerBind *b)
+{
+    if (a->outputType != b->outputType) {
+        return SDL_FALSE;
+    }
+
+    if (a->outputType == SDL_CONTROLLER_BINDTYPE_AXIS) {
+        return (a->output.axis.axis == b->output.axis.axis);
+    } else {
+        return (a->output.button == b->output.button);
+    }
+}
+
+static void ResetOutput(SDL_GameController *gamecontroller, SDL_ExtendedGameControllerBind *bind)
+{
+    if (bind->outputType == SDL_CONTROLLER_BINDTYPE_AXIS) {
+        SDL_PrivateGameControllerAxis(gamecontroller, bind->output.axis.axis, 0);
+    } else {
+        SDL_PrivateGameControllerButton(gamecontroller, bind->output.button, SDL_RELEASED);
+    }
+}
+
+static void HandleJoystickAxis(SDL_GameController *gamecontroller, int axis, int value)
+{
+    int i;
+    SDL_ExtendedGameControllerBind *last_match = gamecontroller->last_match_axis[axis];
+    SDL_ExtendedGameControllerBind *match = NULL;
+
+    for (i = 0; i < gamecontroller->num_bindings; ++i) {
+        SDL_ExtendedGameControllerBind *binding = &gamecontroller->bindings[i];
+        if (binding->inputType == SDL_CONTROLLER_BINDTYPE_AXIS &&
+            axis == binding->input.axis.axis) {
+            if (binding->input.axis.axis_min < binding->input.axis.axis_max) {
+                if (value >= binding->input.axis.axis_min &&
+                    value <= binding->input.axis.axis_max) {
+                    match = binding;
+                    break;
+                }
+            } else {
+                if (value >= binding->input.axis.axis_max &&
+                    value <= binding->input.axis.axis_min) {
+                    match = binding;
+                    break;
+                }
+            }
+        }
+    }
+
+    if (last_match && (!match || !HasSameOutput(last_match, match))) {
+        /* Clear the last input that this axis generated */
+        ResetOutput(gamecontroller, last_match);
+    }
+
+    if (match) {
+        if (match->outputType == SDL_CONTROLLER_BINDTYPE_AXIS) {
+            if (match->input.axis.axis_min != match->output.axis.axis_min || match->input.axis.axis_max != match->output.axis.axis_max) {
+                float normalized_value = (float)(value - match->input.axis.axis_min) / (match->input.axis.axis_max - match->input.axis.axis_min);
+                value = match->output.axis.axis_min + (int)(normalized_value * (match->output.axis.axis_max - match->output.axis.axis_min));
+            }
+            SDL_PrivateGameControllerAxis(gamecontroller, match->output.axis.axis, (Sint16)value);
+        } else {
+            Uint8 state;
+            int threshold = match->input.axis.axis_min + (match->input.axis.axis_max - match->input.axis.axis_min) / 2;
+            if (match->input.axis.axis_max < match->input.axis.axis_min) {
+                state = (value <= threshold) ? SDL_PRESSED : SDL_RELEASED;
+            } else {
+                state = (value >= threshold) ? SDL_PRESSED : SDL_RELEASED;
+            }
+            SDL_PrivateGameControllerButton(gamecontroller, match->output.button, state);
+        }
+    }
+    gamecontroller->last_match_axis[axis] = match;
+}
+
+static void HandleJoystickButton(SDL_GameController *gamecontroller, int button, Uint8 state)
+{
+    int i;
+
+    for (i = 0; i < gamecontroller->num_bindings; ++i) {
+        SDL_ExtendedGameControllerBind *binding = &gamecontroller->bindings[i];
+        if (binding->inputType == SDL_CONTROLLER_BINDTYPE_BUTTON &&
+            button == binding->input.button) {
+            if (binding->outputType == SDL_CONTROLLER_BINDTYPE_AXIS) {
+                int value = state ? binding->output.axis.axis_max : binding->output.axis.axis_min;
+                SDL_PrivateGameControllerAxis(gamecontroller, binding->output.axis.axis, (Sint16)value);
+            } else {
+                SDL_PrivateGameControllerButton(gamecontroller, binding->output.button, state);
+            }
+            break;
+        }
+    }
+}
+
+static void HandleJoystickHat(SDL_GameController *gamecontroller, int hat, Uint8 value)
+{
+    int i;
+    Uint8 last_mask = gamecontroller->last_hat_mask[hat];
+    Uint8 changed_mask = (last_mask ^ value);
+
+    for (i = 0; i < gamecontroller->num_bindings; ++i) {
+        SDL_ExtendedGameControllerBind *binding = &gamecontroller->bindings[i];
+        if (binding->inputType == SDL_CONTROLLER_BINDTYPE_HAT && hat == binding->input.hat.hat) {
+            if ((changed_mask & binding->input.hat.hat_mask) != 0) {
+                if (value & binding->input.hat.hat_mask) {
+                    if (binding->outputType == SDL_CONTROLLER_BINDTYPE_AXIS) {
+                        SDL_PrivateGameControllerAxis(gamecontroller, binding->output.axis.axis, (Sint16)binding->output.axis.axis_max);
+                    } else {
+                        SDL_PrivateGameControllerButton(gamecontroller, binding->output.button, SDL_PRESSED);
+                    }
+                } else {
+                    ResetOutput(gamecontroller, binding);
+                }
+            }
+        }
+    }
+    gamecontroller->last_hat_mask[hat] = value;
+}
+
 /*
  * Event filter to fire controller events from joystick ones
  */
@@ -153,32 +270,10 @@ static int SDL_GameControllerEventWatcher(void *userdata, SDL_Event * event)
     switch(event->type) {
     case SDL_JOYAXISMOTION:
         {
-            SDL_GameController *controllerlist;
-
-            if (event->jaxis.axis >= k_nMaxReverseEntries)
-            {
-                SDL_SetError("SDL_GameControllerEventWatcher: Axis index %d too large, ignoring motion", (int)event->jaxis.axis);
-                break;
-            }
-
-            controllerlist = SDL_gamecontrollers;
+            SDL_GameController *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:
-                                value = value / 2 + 16384;
-                                break;
-                            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);
-                    }
+                    HandleJoystickAxis(controllerlist, event->jaxis.axis, event->jaxis.value);
                     break;
                 }
                 controllerlist = controllerlist->next;
@@ -188,22 +283,10 @@ static int SDL_GameControllerEventWatcher(void *userdata, SDL_Event * event)
     case SDL_JOYBUTTONDOWN:
     case SDL_JOYBUTTONUP:
         {
-            SDL_GameController *controllerlist;
-
-            if (event->jbutton.button >= k_nMaxReverseEntries)
-            {
-                SDL_SetError("SDL_GameControllerEventWatcher: Button index %d too large, ignoring update", (int)event->jbutton.button);
-                break;
-            }
-
-            controllerlist = SDL_gamecontrollers;
+            SDL_GameController *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);
-                    }
+                    HandleJoystickButton(controllerlist, event->jbutton.button, event->jbutton.state);
                     break;
                 }
                 controllerlist = controllerlist->next;
@@ -212,43 +295,10 @@ static int SDL_GameControllerEventWatcher(void *userdata, SDL_Event * event)
         break;
     case SDL_JOYHATMOTION:
         {
-            SDL_GameController *controllerlist;
-
-            if (event->jhat.hat >= 4) break;
-
-            controllerlist = SDL_gamecontrollers;
+            SDL_GameController *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;
-
+                    HandleJoystickHat(controllerlist, event->jhat.hat, event->jhat.value);
                     break;
                 }
                 controllerlist = controllerlist->next;
@@ -321,8 +371,14 @@ static const char* map_StringForControllerAxis[] = {
 SDL_GameControllerAxis SDL_GameControllerGetAxisFromString(const char *pchString)
 {
     int entry;
-    if (!pchString || !pchString[0])
+
+    if (pchString && (*pchString == '+' || *pchString == '-')) {
+        ++pchString;
+    }
+
+    if (!pchString || !pchString[0]) {
         return SDL_CONTROLLER_AXIS_INVALID;
+    }
 
     for (entry = 0; map_StringForControllerAxis[entry]; ++entry) {
         if (!SDL_strcasecmp(pchString, map_StringForControllerAxis[entry]))
@@ -391,63 +447,95 @@ const char* SDL_GameControllerGetStringForButton(SDL_GameControllerButton axis)
 /*
  * given a controller button name and a joystick name update our mapping structure with it
  */
-static void SDL_PrivateGameControllerParseButton(const char *szGameButton, const char *szJoystickButton, struct _SDL_ControllerMapping *pMapping)
+static void SDL_PrivateGameControllerParseElement(SDL_GameController *gamecontroller, const char *szGameButton, const char *szJoystickButton)
 {
-    int iSDLButton = 0;
+    SDL_ExtendedGameControllerBind bind;
     SDL_GameControllerButton button;
     SDL_GameControllerAxis axis;
-    button = SDL_GameControllerGetButtonFromString(szGameButton);
-    axis = SDL_GameControllerGetAxisFromString(szGameButton);
-    iSDLButton = SDL_atoi(&szJoystickButton[1]);
+    SDL_bool invert_input = SDL_FALSE;
+    char half_axis_input = 0;
+    char half_axis_output = 0;
 
-    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;
+    if (*szGameButton == '+' || *szGameButton == '-') {
+        half_axis_output = *szGameButton++;
+    }
+
+    axis = SDL_GameControllerGetAxisFromString(szGameButton);
+    button = SDL_GameControllerGetButtonFromString(szGameButton);
+    if (axis != SDL_CONTROLLER_AXIS_INVALID) {
+        bind.outputType = SDL_CONTROLLER_BINDTYPE_AXIS;
+        bind.output.axis.axis = axis;
+        if (axis == SDL_CONTROLLER_AXIS_TRIGGERLEFT || axis == SDL_CONTROLLER_AXIS_TRIGGERRIGHT) {
+            bind.output.axis.axis_min = 0;
+            bind.output.axis.axis_max = SDL_JOYSTICK_AXIS_MAX;
         } else {
-            SDL_assert(!"How did we get here?");
+            if (half_axis_output == '+') {
+                bind.output.axis.axis_min = 0;
+                bind.output.axis.axis_max = SDL_JOYSTICK_AXIS_MAX;
+            } else if (half_axis_output == '-') {
+                bind.output.axis.axis_min = 0;
+                bind.output.axis.axis_max = SDL_JOYSTICK_AXIS_MIN;
+            } else {
+                bind.output.axis.axis_min = SDL_JOYSTICK_AXIS_MIN;
+                bind.output.axis.axis_max = SDL_JOYSTICK_AXIS_MAX;
+            }
         }
+    } else if (button != SDL_CONTROLLER_BUTTON_INVALID) {
+        bind.outputType = SDL_CONTROLLER_BINDTYPE_BUTTON;
+        bind.output.button = button;
+    } else {
+        SDL_SetError("Unexpected controller element %s", szGameButton);
+        return;
+    }
 
-    } 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;
+    if (*szJoystickButton == '+' || *szJoystickButton == '-') {
+        half_axis_input = *szJoystickButton++;
+    }
+    if (szJoystickButton[SDL_strlen(szJoystickButton) - 1] == '~') {
+        invert_input = SDL_TRUE;
+    }
+
+    if (szJoystickButton[0] == 'a' && SDL_isdigit(szJoystickButton[1])) {
+        bind.inputType = SDL_CONTROLLER_BINDTYPE_AXIS;
+        bind.input.axis.axis = SDL_atoi(&szJoystickButton[1]);
+        if (half_axis_input == '+') {
+            bind.input.axis.axis_min = 0;
+            bind.input.axis.axis_max = SDL_JOYSTICK_AXIS_MAX;
+        } else if (half_axis_input == '-') {
+            bind.input.axis.axis_min = 0;
+            bind.input.axis.axis_max = SDL_JOYSTICK_AXIS_MIN;
         } else {
-            SDL_assert(!"How did we get here?");
+            bind.input.axis.axis_min = SDL_JOYSTICK_AXIS_MIN;
+            bind.input.axis.axis_max = SDL_JOYSTICK_AXIS_MAX;
         }
-    } else if (szJoystickButton[0] == 'h') {
+        if (invert_input) {
+            int tmp = bind.input.axis.axis_min;
+            bind.input.axis.axis_min = bind.input.axis.axis_max;
+            bind.input.axis.axis_max = tmp;
+        }
+    } else if (szJoystickButton[0] == 'b' && SDL_isdigit(szJoystickButton[1])) {
+        bind.inputType = SDL_CONTROLLER_BINDTYPE_BUTTON;
+        bind.input.button = SDL_atoi(&szJoystickButton[1]);
+    } else if (szJoystickButton[0] == 'h' && SDL_isdigit(szJoystickButton[1]) &&
+               szJoystickButton[2] == '.' && SDL_isdigit(szJoystickButton[3])) {
         int hat = SDL_atoi(&szJoystickButton[1]);
         int mask = SDL_atoi(&szJoystickButton[3]);
-        if (hat >= 4) {
-            SDL_SetError("Hat index too large: %d", iSDLButton);
-        }
+        bind.inputType = SDL_CONTROLLER_BINDTYPE_HAT;
+        bind.input.hat.hat = hat;
+        bind.input.hat.hat_mask = mask;
+    } else {
+        SDL_SetError("Unexpected joystick element: %s", szJoystickButton);
+        return;
+    }
 
-        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?");
-        }
+    ++gamecontroller->num_bindings;
+    gamecontroller->bindings = (SDL_ExtendedGameControllerBind *)SDL_realloc(gamecontroller->bindings, gamecontroller->num_bindings * sizeof(*gamecontroller->bindings));
+    if (!gamecontroller->bindings) {
+        gamecontroller->num_bindings = 0;
+        SDL_OutOfMemory();
+        return;
     }
+    gamecontroller->bindings[gamecontroller->num_bindings - 1] = bind;
 }
 
 
@@ -455,7 +543,7 @@ static void SDL_PrivateGameControllerParseButton(const char *szGameButton, const
  * given a controller mapping string update our mapping object
  */
 static void
-SDL_PrivateGameControllerParseControllerConfigString(struct _SDL_ControllerMapping *pMapping, const char *pchString)
+SDL_PrivateGameControllerParseControllerConfigString(SDL_GameController *gamecontroller, const char *pchString)
 {
     char szGameButton[20];
     char szJoystickButton[20];
@@ -463,8 +551,8 @@ SDL_PrivateGameControllerParseControllerConfigString(struct _SDL_ControllerMappi
     int i = 0;
     const char *pchPos = pchString;
 
-    SDL_memset(szGameButton, 0x0, sizeof(szGameButton));
-    SDL_memset(szJoystickButton, 0x0, sizeof(szJoystickButton));
+    SDL_zero(szGameButton);
+    SDL_zero(szJoystickButton);
 
     while (pchPos && *pchPos) {
         if (*pchPos == ':') {
@@ -475,9 +563,9 @@ SDL_PrivateGameControllerParseControllerConfigString(struct _SDL_ControllerMappi
         } 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));
+            SDL_PrivateGameControllerParseElement(gamecontroller, szGameButton, szJoystickButton);
+            SDL_zero(szGameButton);
+            SDL_zero(szJoystickButton);
 
         } else if (bGameButton) {
             if (i >= sizeof(szGameButton)) {
@@ -497,43 +585,37 @@ SDL_PrivateGameControllerParseControllerConfigString(struct _SDL_ControllerMappi
         pchPos++;
     }
 
-    SDL_PrivateGameControllerParseButton(szGameButton, szJoystickButton, pMapping);
+    SDL_PrivateGameControllerParseElement(gamecontroller, szGameButton, szJoystickButton);
 
 }
 
 /*
  * Make a new button mapping struct
  */
-static void SDL_PrivateLoadButtonMapping(struct _SDL_ControllerMapping *pMapping, SDL_JoystickGUID guid, const char *pchName, const char *pchMapping)
+static void SDL_PrivateLoadButtonMapping(SDL_GameController *gamecontroller, 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;
+    int i;
+
+    gamecontroller->guid = guid;
+    gamecontroller->name = pchName;
+    gamecontroller->num_bindings = 0;
+    SDL_memset(gamecontroller->last_match_axis, 0, gamecontroller->joystick->naxes * sizeof(*gamecontroller->last_match_axis));
+
+    SDL_PrivateGameControllerParseControllerConfigString(gamecontroller, pchMapping);
+
+    /* Set the zero point for triggers */
+    for (i = 0; i < gamecontroller->num_bindings; ++i) {
+        SDL_ExtendedGameControllerBind *binding = &gamecontroller->bindings[i];
+        if (binding->inputType == SDL_CONTROLLER_BINDTYPE_AXIS &&
+            binding->outputType == SDL_CONTROLLER_BINDTYPE_AXIS &&
+            (binding->output.axis.axis == SDL_CONTROLLER_AXIS_TRIGGERLEFT ||
+             binding->output.axis.axis == SDL_CONTROLLER_AXIS_TRIGGERRIGHT)) {
+            if (binding->input.axis.axis < gamecontroller->joystick->naxes) {
+                gamecontroller->joystick->axes[binding->input.axis.axis].value =
+                gamecontroller->joystick->axes[binding->input.axis.axis].zero = (Sint16)binding->input.axis.axis_min;
+            }
+        }
     }
-
-    SDL_PrivateGameControllerParseControllerConfigString(pMapping, pchMapping);
 }
 
 
@@ -628,14 +710,14 @@ static void SDL_PrivateGameControllerRefreshMapping(ControllerMapping_t *pContro
 {
     SDL_GameController *gamecontrollerlist = SDL_gamecontrollers;
     while (gamecontrollerlist) {
-        if (!SDL_memcmp(&gamecontrollerlist->mapping.guid, &pControllerMapping->guid, sizeof(pControllerMapping->guid))) {
+        if (!SDL_memcmp(&gamecontrollerlist->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);
+            SDL_PrivateLoadButtonMapping(gamecontrollerlist, pControllerMapping->guid, pControllerMapping->name, pControllerMapping->mapping);
         }
 
         gamecontrollerlist = gamecontrollerlist->next;
@@ -972,7 +1054,7 @@ SDL_GameControllerMapping(SDL_GameController * gamecontroller)
         return NULL;
     }
 
-    return SDL_GameControllerMappingForGUID(gamecontroller->mapping.guid);
+    return SDL_GameControllerMappingForGUID(gamecontroller->guid);
 }
 
 static void
@@ -1046,7 +1128,7 @@ SDL_GameControllerInit(void)
 const char *
 SDL_GameControllerNameForIndex(int device_index)
 {
-    ControllerMapping_t *pSupportedController =  SDL_PrivateGetControllerMapping(device_index);
+    ControllerMapping_t *pSupportedController = SDL_PrivateGetControllerMapping(device_index);
     if (pSupportedController) {
         return pSupportedController->name;
     }
@@ -1060,11 +1142,10 @@ SDL_GameControllerNameForIndex(int device_index)
 SDL_bool
 SDL_IsGameController(int device_index)
 {
-    ControllerMapping_t *pSupportedController =  SDL_PrivateGetControllerMapping(device_index);
+    ControllerMapping_t *pSupportedController = SDL_PrivateGetControllerMapping(device_index);
     if (pSupportedController) {
         return SDL_TRUE;
     }
-
     return SDL_FALSE;
 }
 
@@ -1110,14 +1191,13 @@ SDL_GameControllerOpen(int device_index)
     }
 
     /* Create and initialize the joystick */
-    gamecontroller = (SDL_GameController *) SDL_malloc((sizeof *gamecontroller));
+    gamecontroller = (SDL_GameController *) SDL_calloc(1, sizeof(*gamecontroller));
     if (gamecontroller == NULL) {
         SDL_OutOfMemory();
         SDL_UnlockJoystickList();
         return NULL;
     }
 
-    SDL_memset(gamecontroller, 0, (sizeof *gamecontroller));
     gamecontroller->joystick = SDL_JoystickOpen(device_index);
     if (!gamecontroller->joystick) {
         SDL_free(gamecontroller);
@@ -1125,21 +1205,10 @@ SDL_GameControllerOpen(int device_index)
         return NULL;
     }
 
-    SDL_PrivateLoadButtonMapping(&gamecontroller->mapping, pSupportedController->guid, pSupportedController->name, pSupportedController->mapping);
+    gamecontroller->last_match_axis = (SDL_ExtendedGameControllerBind **)SDL_calloc(gamecontroller->joystick->naxes, sizeof(*gamecontroller->last_match_axis));
+    gamecontroller->last_hat_mask = (Uint8 *)SDL_calloc(gamecontroller->joystick->nhats, sizeof(*gamecontroller->last_hat_mask));
 
-    /* The triggers are mapped from -32768 to 32767, where -32768 is the 'unpressed' value */
-    {
-        int leftTriggerMapping = gamecontroller->mapping.axes[SDL_CONTROLLER_AXIS_TRIGGERLEFT];
-        int rightTriggerMapping = gamecontroller->mapping.axes[SDL_CONTROLLER_AXIS_TRIGGERRIGHT];
-        if (leftTriggerMapping >= 0) {
-            gamecontroller->joystick->axes[leftTriggerMapping].value =
-            gamecontroller->joystick->axes[leftTriggerMapping].zero = (Sint16)-32768;
-        }
-        if (rightTriggerMapping >= 0) {
-            gamecontroller->joystick->axes[rightTriggerMapping].value =
-            gamecontroller->joystick->axes[rightTriggerMapping].zero = (Sint16)-32768;
-        }
-    }
+    SDL_PrivateLoadButtonMapping(gamecontroller, pSupportedController->guid, pSupportedController->name, pSupportedController->mapping);
 
     /* Add joystick to list */
     ++gamecontroller->ref_count;
@@ -1162,65 +1231,102 @@ SDL_GameControllerUpdate(void)
     SDL_JoystickUpdate();
 }
 
-
 /*
  * Get the current state of an axis control on a controller
  */
 Sint16
 SDL_GameControllerGetAxis(SDL_GameController * gamecontroller, SDL_GameControllerAxis axis)
 {
+    int i;
+
     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;
+    for (i = 0; i < gamecontroller->num_bindings; ++i) {
+        SDL_ExtendedGameControllerBind *binding = &gamecontroller->bindings[i];
+        if (binding->outputType == SDL_CONTROLLER_BINDTYPE_AXIS && binding->output.axis.axis == axis) {
+            int value = 0;
+            SDL_bool valid_input_range;
+            SDL_bool valid_output_range;
+
+            if (binding->inputType == SDL_CONTROLLER_BINDTYPE_AXIS) {
+                value = SDL_JoystickGetAxis(gamecontroller->joystick, binding->input.axis.axis);
+                if (binding->input.axis.axis_min < binding->input.axis.axis_max) {
+                    valid_input_range = (value >= binding->input.axis.axis_min && value <= binding->input.axis.axis_max);
+                } else {
+                    valid_input_range = (value >= binding->input.axis.axis_max && value <= binding->input.axis.axis_min);
+                }
+                if (valid_input_range) {
+                    if (binding->input.axis.axis_min != binding->output.axis.axis_min || binding->input.axis.axis_max != binding->output.axis.axis_max) {
+                        float normalized_value = (float)(value - binding->input.axis.axis_min) / (binding->input.axis.axis_max - binding->input.axis.axis_min);
+                        value = binding->output.axis.axis_min + (int)(normalized_value * (binding->output.axis.axis_max - binding->output.axis.axis_min));
+                    }
+                }
+            } else if (binding->inputType == SDL_CONTROLLER_BINDTYPE_BUTTON) {
+                value = SDL_JoystickGetButton(gamecontroller->joystick, binding->input.button);
+                if (value == SDL_PRESSED) {
+                    value = binding->output.axis.axis_max;
+                }
+            } else if (binding->inputType == SDL_CONTROLLER_BINDTYPE_HAT) {
+                int hat_mask = SDL_JoystickGetHat(gamecontroller->joystick, binding->input.hat.hat);
+                if (hat_mask & binding->input.hat.hat_mask) {
+                    value = binding->output.axis.axis_max;
+                }
+            }
+
+            if (binding->output.axis.axis_min < binding->output.axis.axis_max) {
+                valid_output_range = (value >= binding->output.axis.axis_min && value <= binding->output.axis.axis_max);
+            } else {
+                valid_output_range = (value >= binding->output.axis.axis_max && value <= binding->output.axis.axis_min);
+            }
+            // If the value is zero, there might be another binding that makes it non-zero
+            if (value != 0 && valid_output_range) {
+                return (Sint16)value;
+            }
         }
-        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;
+    int i;
 
-    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;
+    if (!gamecontroller)
         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;
+    for (i = 0; i < gamecontroller->num_bindings; ++i) {
+        SDL_ExtendedGameControllerBind *binding = &gamecontroller->bindings[i];
+        if (binding->outputType == SDL_CONTROLLER_BINDTYPE_BUTTON && binding->output.button == button) {
+            if (binding->inputType == SDL_CONTROLLER_BINDTYPE_AXIS) {
+                SDL_bool valid_input_range;
+
+                int value = SDL_JoystickGetAxis(gamecontroller->joystick, binding->input.axis.axis);
+                int threshold = binding->input.axis.axis_min + (binding->input.axis.axis_max - binding->input.axis.axis_min) / 2;
+                if (binding->input.axis.axis_min < binding->input.axis.axis_max) {
+                    valid_input_range = (value >= binding->input.axis.axis_min && value <= binding->input.axis.axis_max);
+                    if (valid_input_range) {
+                        return (value >= threshold) ? SDL_PRESSED : SDL_RELEASED;
+                    }
+                } else {
+                    valid_input_range = (value >= binding->input.axis.axis_max && value <= binding->input.axis.axis_min);
+                    if (valid_input_range) {
+                        return (value <= threshold) ? SDL_PRESSED : SDL_RELEASED;
+                    }
+                }
+            } else if (binding->inputType == SDL_CONTROLLER_BINDTYPE_BUTTON) {
+                return SDL_JoystickGetButton(gamecontroller->joystick, binding->input.button);
+            } else if (binding->inputType == SDL_CONTROLLER_BINDTYPE_HAT) {
+                int hat_mask = SDL_JoystickGetHat(gamecontroller->joystick, binding->input.hat.hat);
+                return (hat_mask & binding->input.hat.hat_mask) ? SDL_PRESSED : SDL_RELEASED;
+            }
+        }
     }
-
-    return 0;
+    return SDL_RELEASED;
 }
 
 const char *
@@ -1229,7 +1335,7 @@ SDL_GameControllerName(SDL_GameController * gamecontroller)
     if (!gamecontroller)
         return NULL;
 
-    return gamecontroller->mapping.name;
+    return gamecontroller->name;
 }
 
 Uint16
@@ -1302,20 +1408,29 @@ SDL_GameControllerFromInstanceID(SDL_JoystickID joyid)
  */
 SDL_GameControllerButtonBind SDL_GameControllerGetBindForAxis(SDL_GameController * gamecontroller, SDL_GameControllerAxis axis)
 {
+    int i;
     SDL_GameControllerButtonBind bind;
-    SDL_memset(&bind, 0x0, sizeof(bind));
+    SDL_zero(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];
+    for (i = 0; i < gamecontroller->num_bindings; ++i) {
+        SDL_ExtendedGameControllerBind *binding = &gamecontroller->bindings[i];
+        if (binding->outputType == SDL_CONTROLLER_BINDTYPE_AXIS && binding->output.axis.axis == axis) {
+            bind.bindType = binding->inputType;
+            if (binding->inputType == SDL_CONTROLLER_BINDTYPE_AXIS) {
+                /* FIXME: There might be multiple axes bound now that we have axis ranges... */
+                bind.value.axis = binding->input.axis.axis;
+            } else if (binding->inputType == SDL_CONTROLLER_BINDTYPE_BUTTON) {
+                bind.value.button = binding->input.button;
+            } else if (binding->inputType == SDL_CONTROLLER_BINDTYPE_HAT) {
+                bind.value.hat.hat = binding->input.hat.hat;
+                bind.value.hat.hat_mask = binding->input.hat.hat_mask;
+            }
+            break;
+        }
     }
-
     return bind;
 }
 
@@ -1325,24 +1440,28 @@ SDL_GameControllerButtonBind SDL_GameControllerGetBindForAxis(SDL_GameController
  */
 SDL_GameControllerButtonBind SDL_GameControllerGetBindForButton(SDL_GameController * gamecontroller, SDL_GameControllerButton button)
 {
+    int i;
     SDL_GameControllerButtonBind bind;
-    SDL_memset(&bind, 0x0, sizeof(bind));
+    SDL_zero(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;
+    for (i = 0; i < gamecontroller->num_bindings; ++i) {
+        SDL_ExtendedGameControllerBind *binding = &gamecontroller->bindings[i];
+        if (binding->outputType == SDL_CONTROLLER_BINDTYPE_BUTTON && binding->output.button == button) {
+            bind.bindType = binding->inputType;
+            if (binding->inputType == SDL_CONTROLLER_BINDTYPE_AXIS) {
+                bind.value.axis = binding->input.axis.axis;
+            } else if (binding->inputType == SDL_CONTROLLER_BINDTYPE_BUTTON) {
+                bind.value.button = binding->input.button;
+            } else if (binding->inputType == SDL_CONTROLLER_BINDTYPE_HAT) {
+                bind.value.hat.hat = binding->input.hat.hat;
+                bind.value.hat.hat_mask = binding->input.hat.hat_mask;
+            }
+            break;
+        }
     }
-
     return bind;
 }
 
@@ -1381,6 +1500,9 @@ SDL_GameControllerClose(SDL_GameController * gamecontroller)
         gamecontrollerlist = gamecontrollerlist->next;
     }
 
+    SDL_free(gamecontroller->bindings);
+    SDL_free(gamecontroller->last_match_axis);
+    SDL_free(gamecontroller->last_hat_mask);
     SDL_free(gamecontroller);
 
     SDL_UnlockJoystickList();
@@ -1417,7 +1539,7 @@ SDL_GameControllerQuit(void)
 /*
  * Event filter to transform joystick events into appropriate game controller ones
  */
-int
+static int
 SDL_PrivateGameControllerAxis(SDL_GameController * gamecontroller, SDL_GameControllerAxis axis, Sint16 value)
 {
     int posted;
@@ -1441,7 +1563,7 @@ SDL_PrivateGameControllerAxis(SDL_GameController * gamecontroller, SDL_GameContr
 /*
  * Event filter to transform joystick events into appropriate game controller ones
  */
-int
+static int
 SDL_PrivateGameControllerButton(SDL_GameController * gamecontroller, SDL_GameControllerButton button, Uint8 state)
 {
     int posted;

+ 3 - 5
src/joystick/windows/SDL_dinputjoystick.c

@@ -33,9 +33,7 @@
 #endif
 
 #define INPUT_QSIZE 32      /* Buffer up to 32 input messages */
-#define AXIS_MIN    -32768  /* minimum value for axis coordinate */
-#define AXIS_MAX    32767   /* maximum value for axis coordinate */
-#define JOY_AXIS_THRESHOLD  (((AXIS_MAX)-(AXIS_MIN))/100)   /* 1% motion */
+#define JOY_AXIS_THRESHOLD  (((SDL_JOYSTICK_AXIS_MAX)-(SDL_JOYSTICK_AXIS_MIN))/100)   /* 1% motion */
 
 /* external variables referenced. */
 extern HWND SDL_HelperWindow;
@@ -481,8 +479,8 @@ EnumDevObjectsCallback(LPCDIDEVICEOBJECTINSTANCE dev, LPVOID pvRef)
         diprg.diph.dwHeaderSize = sizeof(diprg.diph);
         diprg.diph.dwObj = dev->dwType;
         diprg.diph.dwHow = DIPH_BYID;
-        diprg.lMin = AXIS_MIN;
-        diprg.lMax = AXIS_MAX;
+        diprg.lMin = SDL_JOYSTICK_AXIS_MIN;
+        diprg.lMax = SDL_JOYSTICK_AXIS_MAX;
 
         result =
             IDirectInputDevice8_SetProperty(joystick->hwdata->InputDevice,

+ 3 - 5
src/joystick/windows/SDL_mmjoystick.c

@@ -41,10 +41,8 @@
 #define MAX_JOYSTICKS   16
 #define MAX_AXES    6       /* each joystick can have up to 6 axes */
 #define MAX_BUTTONS 32      /* and 32 buttons                      */
-#define AXIS_MIN    -32768  /* minimum value for axis coordinate */
-#define AXIS_MAX    32767   /* maximum value for axis coordinate */
 /* limit axis to 256 possible positions to filter out noise */
-#define JOY_AXIS_THRESHOLD      (((AXIS_MAX)-(AXIS_MIN))/256)
+#define JOY_AXIS_THRESHOLD      (((SDL_JOYSTICK_AXIS_MAX)-(SDL_JOYSTICK_AXIS_MIN))/256)
 #define JOY_BUTTON_FLAG(n)  (1<<n)
 
 
@@ -253,9 +251,9 @@ SDL_SYS_JoystickOpen(SDL_Joystick * joystick, int device_index)
     joystick->hwdata->id = SYS_JoystickID[index];
     for (i = 0; i < MAX_AXES; ++i) {
         if ((i < 2) || (SYS_Joystick[index].wCaps & caps_flags[i - 2])) {
-            joystick->hwdata->transaxis[i].offset = AXIS_MIN - axis_min[i];
+            joystick->hwdata->transaxis[i].offset = SDL_JOYSTICK_AXIS_MIN - axis_min[i];
             joystick->hwdata->transaxis[i].scale =
-                (float) (AXIS_MAX - AXIS_MIN) / (axis_max[i] - axis_min[i]);
+                (float) (SDL_JOYSTICK_AXIS_MAX - SDL_JOYSTICK_AXIS_MIN) / (axis_max[i] - axis_min[i]);
         } else {
             joystick->hwdata->transaxis[i].offset = 0;
             joystick->hwdata->transaxis[i].scale = 1.0; /* Just in case */

BIN
test/axis.bmp


+ 198 - 52
test/controllermap.c

@@ -32,7 +32,22 @@
 #define MARKER_BUTTON 1
 #define MARKER_AXIS 2
 
-#define BINDING_COUNT (SDL_CONTROLLER_BUTTON_MAX + SDL_CONTROLLER_AXIS_MAX)
+enum
+{
+    SDL_CONTROLLER_BINDING_AXIS_LEFTX_NEGATIVE,
+    SDL_CONTROLLER_BINDING_AXIS_LEFTX_POSITIVE,
+    SDL_CONTROLLER_BINDING_AXIS_LEFTY_NEGATIVE,
+    SDL_CONTROLLER_BINDING_AXIS_LEFTY_POSITIVE,
+    SDL_CONTROLLER_BINDING_AXIS_RIGHTX_NEGATIVE,
+    SDL_CONTROLLER_BINDING_AXIS_RIGHTX_POSITIVE,
+    SDL_CONTROLLER_BINDING_AXIS_RIGHTY_NEGATIVE,
+    SDL_CONTROLLER_BINDING_AXIS_RIGHTY_POSITIVE,
+    SDL_CONTROLLER_BINDING_AXIS_TRIGGERLEFT,
+    SDL_CONTROLLER_BINDING_AXIS_TRIGGERRIGHT,
+    SDL_CONTROLLER_BINDING_AXIS_MAX,
+};
+
+#define BINDING_COUNT (SDL_CONTROLLER_BUTTON_MAX + SDL_CONTROLLER_BINDING_AXIS_MAX)
 
 static struct 
 {
@@ -56,12 +71,16 @@ static struct
     { 154, 249, 0.0, MARKER_BUTTON }, /* SDL_CONTROLLER_BUTTON_DPAD_DOWN */
     { 116, 217, 0.0, MARKER_BUTTON }, /* SDL_CONTROLLER_BUTTON_DPAD_LEFT */
     { 186, 217, 0.0, MARKER_BUTTON }, /* SDL_CONTROLLER_BUTTON_DPAD_RIGHT */
-    {  75, 154, 0.0,   MARKER_AXIS }, /* SDL_CONTROLLER_AXIS_LEFTX */
-    {  75, 154, 90.0,  MARKER_AXIS }, /* SDL_CONTROLLER_AXIS_LEFTY */
-    { 305, 230, 0.0,   MARKER_AXIS }, /* SDL_CONTROLLER_AXIS_RIGHTX */
-    { 305, 230, 90.0,  MARKER_AXIS }, /* SDL_CONTROLLER_AXIS_RIGHTY */
-    {  91,   0, 90.0,  MARKER_AXIS }, /* SDL_CONTROLLER_AXIS_TRIGGERLEFT */
-    { 375,   0, 90.0,  MARKER_AXIS }, /* SDL_CONTROLLER_AXIS_TRIGGERRIGHT */
+    {  74, 153, 270.0, MARKER_AXIS }, /* SDL_CONTROLLER_BINDING_AXIS_LEFTX_NEGATIVE */
+    {  74, 153, 90.0,  MARKER_AXIS }, /* SDL_CONTROLLER_BINDING_AXIS_LEFTX_POSITIVE */
+    {  74, 153, 0.0,   MARKER_AXIS }, /* SDL_CONTROLLER_BINDING_AXIS_LEFTY_NEGATIVE */
+    {  74, 153, 180.0, MARKER_AXIS }, /* SDL_CONTROLLER_BINDING_AXIS_LEFTY_POSITIVE */
+    { 306, 231, 270.0, MARKER_AXIS }, /* SDL_CONTROLLER_BINDING_AXIS_RIGHTX_NEGATIVE */
+    { 306, 231, 90.0,  MARKER_AXIS }, /* SDL_CONTROLLER_BINDING_AXIS_RIGHTX_POSITIVE */
+    { 306, 231, 0.0,   MARKER_AXIS }, /* SDL_CONTROLLER_BINDING_AXIS_RIGHTY_NEGATIVE */
+    { 306, 231, 180.0, MARKER_AXIS }, /* SDL_CONTROLLER_BINDING_AXIS_RIGHTY_POSITIVE */
+    {  91, -20, 180.0, MARKER_AXIS }, /* SDL_CONTROLLER_BINDING_AXIS_TRIGGERLEFT */
+    { 375, -20, 180.0, MARKER_AXIS }, /* SDL_CONTROLLER_BINDING_AXIS_TRIGGERRIGHT */
 };
 
 static int s_arrBindingOrder[BINDING_COUNT] = {
@@ -69,16 +88,20 @@ static int s_arrBindingOrder[BINDING_COUNT] = {
     SDL_CONTROLLER_BUTTON_B,
     SDL_CONTROLLER_BUTTON_Y,
     SDL_CONTROLLER_BUTTON_X,
-    SDL_CONTROLLER_BUTTON_MAX + SDL_CONTROLLER_AXIS_LEFTX,
-    SDL_CONTROLLER_BUTTON_MAX + SDL_CONTROLLER_AXIS_LEFTY,
+    SDL_CONTROLLER_BUTTON_MAX + SDL_CONTROLLER_BINDING_AXIS_LEFTX_NEGATIVE,
+    SDL_CONTROLLER_BUTTON_MAX + SDL_CONTROLLER_BINDING_AXIS_LEFTX_POSITIVE,
+    SDL_CONTROLLER_BUTTON_MAX + SDL_CONTROLLER_BINDING_AXIS_LEFTY_NEGATIVE,
+    SDL_CONTROLLER_BUTTON_MAX + SDL_CONTROLLER_BINDING_AXIS_LEFTY_POSITIVE,
     SDL_CONTROLLER_BUTTON_LEFTSTICK,
-    SDL_CONTROLLER_BUTTON_MAX + SDL_CONTROLLER_AXIS_RIGHTX,
-    SDL_CONTROLLER_BUTTON_MAX + SDL_CONTROLLER_AXIS_RIGHTY,
+    SDL_CONTROLLER_BUTTON_MAX + SDL_CONTROLLER_BINDING_AXIS_RIGHTX_NEGATIVE,
+    SDL_CONTROLLER_BUTTON_MAX + SDL_CONTROLLER_BINDING_AXIS_RIGHTX_POSITIVE,
+    SDL_CONTROLLER_BUTTON_MAX + SDL_CONTROLLER_BINDING_AXIS_RIGHTY_NEGATIVE,
+    SDL_CONTROLLER_BUTTON_MAX + SDL_CONTROLLER_BINDING_AXIS_RIGHTY_POSITIVE,
     SDL_CONTROLLER_BUTTON_RIGHTSTICK,
     SDL_CONTROLLER_BUTTON_LEFTSHOULDER,
-    SDL_CONTROLLER_BUTTON_MAX + SDL_CONTROLLER_AXIS_TRIGGERLEFT,
+    SDL_CONTROLLER_BUTTON_MAX + SDL_CONTROLLER_BINDING_AXIS_TRIGGERLEFT,
     SDL_CONTROLLER_BUTTON_RIGHTSHOULDER,
-    SDL_CONTROLLER_BUTTON_MAX + SDL_CONTROLLER_AXIS_TRIGGERRIGHT,
+    SDL_CONTROLLER_BUTTON_MAX + SDL_CONTROLLER_BINDING_AXIS_TRIGGERRIGHT,
     SDL_CONTROLLER_BUTTON_DPAD_UP,
     SDL_CONTROLLER_BUTTON_DPAD_RIGHT,
     SDL_CONTROLLER_BUTTON_DPAD_DOWN,
@@ -88,7 +111,40 @@ static int s_arrBindingOrder[BINDING_COUNT] = {
     SDL_CONTROLLER_BUTTON_START,
 };
 
-static SDL_GameControllerButtonBind s_arrBindings[BINDING_COUNT];
+typedef struct
+{
+    SDL_GameControllerBindType bindType;
+    union
+    {
+        int button;
+
+        struct {
+            int axis;
+            int axis_min;
+            int axis_max;
+        } axis;
+
+        struct {
+            int hat;
+            int hat_mask;
+        } hat;
+
+    } value;
+
+} SDL_GameControllerExtendedBind;
+
+static SDL_GameControllerExtendedBind s_arrBindings[BINDING_COUNT];
+
+typedef struct
+{
+    SDL_bool m_bMoving;
+    int m_nStartingValue;
+    int m_nFarthestValue;
+} AxisState;
+
+static int s_nNumAxes;
+static AxisState *s_arrAxisState;
+    
 static int s_iCurrentBinding;
 static Uint32 s_unPendingAdvanceTime;
 static SDL_bool s_bBindingComplete;
@@ -110,23 +166,6 @@ LoadTexture(SDL_Renderer *renderer, const char *file, SDL_bool transparent)
     if (transparent) {
         if (temp->format->palette) {
             SDL_SetColorKey(temp, SDL_TRUE, *(Uint8 *) temp->pixels);
-        } else {
-            switch (temp->format->BitsPerPixel) {
-            case 15:
-                SDL_SetColorKey(temp, SDL_TRUE,
-                                (*(Uint16 *) temp->pixels) & 0x00007FFF);
-                break;
-            case 16:
-                SDL_SetColorKey(temp, SDL_TRUE, *(Uint16 *) temp->pixels);
-                break;
-            case 24:
-                SDL_SetColorKey(temp, SDL_TRUE,
-                                (*(Uint32 *) temp->pixels) & 0x00FFFFFF);
-                break;
-            case 32:
-                SDL_SetColorKey(temp, SDL_TRUE, *(Uint32 *) temp->pixels);
-                break;
-            }
         }
     }
 
@@ -143,9 +182,22 @@ LoadTexture(SDL_Renderer *renderer, const char *file, SDL_bool transparent)
     return texture;
 }
 
-void SetCurrentBinding(int iBinding)
+static int
+StandardizeAxisValue(int nValue)
 {
-    SDL_GameControllerButtonBind *pBinding;
+    if (nValue > SDL_JOYSTICK_AXIS_MAX/2) {
+        return SDL_JOYSTICK_AXIS_MAX;
+    } else if (nValue < SDL_JOYSTICK_AXIS_MIN/2) {
+        return SDL_JOYSTICK_AXIS_MIN;
+    } else {
+        return 0;
+    }
+}
+
+static void
+SetCurrentBinding(int iBinding)
+{
+    SDL_GameControllerExtendedBind *pBinding;
 
     if (iBinding < 0) {
         return;
@@ -161,14 +213,15 @@ void SetCurrentBinding(int iBinding)
     pBinding = &s_arrBindings[s_arrBindingOrder[s_iCurrentBinding]];
     SDL_zerop(pBinding);
 
+    SDL_memset(s_arrAxisState, 0, s_nNumAxes*sizeof(*s_arrAxisState));
+
     s_unPendingAdvanceTime = 0;
 }
 
-
 static void
-ConfigureBinding(const SDL_GameControllerButtonBind *pBinding)
+ConfigureBinding(const SDL_GameControllerExtendedBind *pBinding)
 {
-    SDL_GameControllerButtonBind *pCurrent;
+    SDL_GameControllerExtendedBind *pCurrent;
     int iIndex;
     int iCurrentElement = s_arrBindingOrder[s_iCurrentBinding];
 
@@ -221,6 +274,24 @@ ConfigureBinding(const SDL_GameControllerButtonBind *pBinding)
     s_unPendingAdvanceTime = SDL_GetTicks();
 }
 
+static SDL_bool
+BMergeAxisBindings(int iIndex)
+{
+    SDL_GameControllerExtendedBind *pBindingA = &s_arrBindings[iIndex];
+    SDL_GameControllerExtendedBind *pBindingB = &s_arrBindings[iIndex+1];
+    if (pBindingA->bindType == SDL_CONTROLLER_BINDTYPE_AXIS &&
+        pBindingB->bindType == SDL_CONTROLLER_BINDTYPE_AXIS &&
+        pBindingA->value.axis.axis == pBindingB->value.axis.axis) {
+        if (pBindingA->value.axis.axis_min == pBindingB->value.axis.axis_min) {
+            pBindingA->value.axis.axis_min = pBindingA->value.axis.axis_max;
+            pBindingA->value.axis.axis_max = pBindingB->value.axis.axis_max;
+            pBindingB->bindType = SDL_CONTROLLER_BINDTYPE_NONE;
+            return SDL_TRUE;
+        }
+    }
+    return SDL_FALSE;
+}
+
 static void
 WatchJoystick(SDL_Joystick * joystick)
 {
@@ -279,6 +350,9 @@ WatchJoystick(SDL_Joystick * joystick)
 
     nJoystickID = SDL_JoystickInstanceID(joystick);
 
+    s_nNumAxes = SDL_JoystickNumAxes(joystick);
+    s_arrAxisState = SDL_calloc(s_nNumAxes, sizeof(*s_arrAxisState));
+
     /* Loop, getting joystick events! */
     while (!done && !s_bBindingComplete) {
         int iElement = s_arrBindingOrder[s_iCurrentBinding];
@@ -326,26 +400,35 @@ WatchJoystick(SDL_Joystick * joystick)
                 break;
             case SDL_JOYAXISMOTION:
                 if (event.jaxis.which == nJoystickID) {
-                    uint32_t unAxisMask = (1 << event.jaxis.axis);
-                    SDL_bool bDeflected = (event.jaxis.value <= -20000 || event.jaxis.value >= 20000);
-                    if (bDeflected && !(unDeflectedAxes & unAxisMask)) {
-                        SDL_GameControllerButtonBind binding;
+                    AxisState *pAxisState = &s_arrAxisState[event.jaxis.axis];
+                    int nValue = event.jaxis.value;
+                    int nCurrentDistance, nFarthestDistance;
+                    if (!pAxisState->m_bMoving) {
+                        pAxisState->m_bMoving = SDL_TRUE;
+                        pAxisState->m_nStartingValue = nValue;
+                        pAxisState->m_nFarthestValue = nValue;
+                    }
+                    nCurrentDistance = SDL_abs(nValue - pAxisState->m_nStartingValue);
+                    nFarthestDistance = SDL_abs(pAxisState->m_nFarthestValue - pAxisState->m_nStartingValue);
+                    if (nCurrentDistance > nFarthestDistance) {
+                        pAxisState->m_nFarthestValue = nValue;
+                    }
+                    if (nCurrentDistance < 10000 && nFarthestDistance > 20000) {
+                        /* We've gone out and back, let's bind this axis */
+                        SDL_GameControllerExtendedBind binding;
                         SDL_zero(binding);
                         binding.bindType = SDL_CONTROLLER_BINDTYPE_AXIS;
-                        binding.value.axis = event.jaxis.axis;
+                        binding.value.axis.axis = event.jaxis.axis;
+                        binding.value.axis.axis_min = StandardizeAxisValue(pAxisState->m_nStartingValue);
+                        binding.value.axis.axis_max = StandardizeAxisValue(pAxisState->m_nFarthestValue);
                         ConfigureBinding(&binding);
                     }
-                    if (bDeflected) {
-                        unDeflectedAxes |= unAxisMask;
-                    } else {
-                        unDeflectedAxes &= ~unAxisMask;
-                    }
                 }
                 break;
             case SDL_JOYHATMOTION:
                 if (event.jhat.which == nJoystickID) {
                     if (event.jhat.value != SDL_HAT_CENTERED) {
-                        SDL_GameControllerButtonBind binding;
+                        SDL_GameControllerExtendedBind binding;
                         SDL_zero(binding);
                         binding.bindType = SDL_CONTROLLER_BINDTYPE_HAT;
                         binding.value.hat.hat = event.jhat.hat;
@@ -358,7 +441,7 @@ WatchJoystick(SDL_Joystick * joystick)
                 break;
             case SDL_JOYBUTTONDOWN:
                 if (event.jbutton.which == nJoystickID) {
-                    SDL_GameControllerButtonBind binding;
+                    SDL_GameControllerExtendedBind binding;
                     SDL_zero(binding);
                     binding.bindType = SDL_CONTROLLER_BINDTYPE_BUTTON;
                     binding.value.button = event.jbutton.button;
@@ -430,7 +513,7 @@ WatchJoystick(SDL_Joystick * joystick)
         SDL_strlcat(mapping, ",", SDL_arraysize(mapping));
 
         for (iIndex = 0; iIndex < SDL_arraysize(s_arrBindings); ++iIndex) {
-            SDL_GameControllerButtonBind *pBinding = &s_arrBindings[iIndex];
+            SDL_GameControllerExtendedBind *pBinding = &s_arrBindings[iIndex];
             if (pBinding->bindType == SDL_CONTROLLER_BINDTYPE_NONE) {
                 continue;
             }
@@ -439,8 +522,56 @@ WatchJoystick(SDL_Joystick * joystick)
                 SDL_GameControllerButton eButton = (SDL_GameControllerButton)iIndex;
                 SDL_strlcat(mapping, SDL_GameControllerGetStringForButton(eButton), SDL_arraysize(mapping));
             } else {
-                SDL_GameControllerAxis eAxis = (SDL_GameControllerAxis)(iIndex - SDL_CONTROLLER_BUTTON_MAX);
-                SDL_strlcat(mapping, SDL_GameControllerGetStringForAxis(eAxis), SDL_arraysize(mapping));
+                const char *pszAxisName;
+                switch (iIndex - SDL_CONTROLLER_BUTTON_MAX) {
+                case SDL_CONTROLLER_BINDING_AXIS_LEFTX_NEGATIVE:
+                    if (!BMergeAxisBindings(iIndex)) {
+                        SDL_strlcat(mapping, "-", SDL_arraysize(mapping));
+                    }
+                    pszAxisName = SDL_GameControllerGetStringForAxis(SDL_CONTROLLER_AXIS_LEFTX);
+                    break;
+                case SDL_CONTROLLER_BINDING_AXIS_LEFTX_POSITIVE:
+                    SDL_strlcat(mapping, "+", SDL_arraysize(mapping));
+                    pszAxisName = SDL_GameControllerGetStringForAxis(SDL_CONTROLLER_AXIS_LEFTX);
+                    break;
+                case SDL_CONTROLLER_BINDING_AXIS_LEFTY_NEGATIVE:
+                    if (!BMergeAxisBindings(iIndex)) {
+                        SDL_strlcat(mapping, "-", SDL_arraysize(mapping));
+                    }
+                    pszAxisName = SDL_GameControllerGetStringForAxis(SDL_CONTROLLER_AXIS_LEFTY);
+                    break;
+                case SDL_CONTROLLER_BINDING_AXIS_LEFTY_POSITIVE:
+                    SDL_strlcat(mapping, "+", SDL_arraysize(mapping));
+                    pszAxisName = SDL_GameControllerGetStringForAxis(SDL_CONTROLLER_AXIS_LEFTY);
+                    break;
+                case SDL_CONTROLLER_BINDING_AXIS_RIGHTX_NEGATIVE:
+                    if (!BMergeAxisBindings(iIndex)) {
+                        SDL_strlcat(mapping, "-", SDL_arraysize(mapping));
+                    }
+                    pszAxisName = SDL_GameControllerGetStringForAxis(SDL_CONTROLLER_AXIS_RIGHTX);
+                    break;
+                case SDL_CONTROLLER_BINDING_AXIS_RIGHTX_POSITIVE:
+                    SDL_strlcat(mapping, "+", SDL_arraysize(mapping));
+                    pszAxisName = SDL_GameControllerGetStringForAxis(SDL_CONTROLLER_AXIS_RIGHTX);
+                    break;
+                case SDL_CONTROLLER_BINDING_AXIS_RIGHTY_NEGATIVE:
+                    if (!BMergeAxisBindings(iIndex)) {
+                        SDL_strlcat(mapping, "-", SDL_arraysize(mapping));
+                    }
+                    pszAxisName = SDL_GameControllerGetStringForAxis(SDL_CONTROLLER_AXIS_RIGHTY);
+                    break;
+                case SDL_CONTROLLER_BINDING_AXIS_RIGHTY_POSITIVE:
+                    SDL_strlcat(mapping, "+", SDL_arraysize(mapping));
+                    pszAxisName = SDL_GameControllerGetStringForAxis(SDL_CONTROLLER_AXIS_RIGHTY);
+                    break;
+                case SDL_CONTROLLER_BINDING_AXIS_TRIGGERLEFT:
+                    pszAxisName = SDL_GameControllerGetStringForAxis(SDL_CONTROLLER_AXIS_TRIGGERLEFT);
+                    break;
+                case SDL_CONTROLLER_BINDING_AXIS_TRIGGERRIGHT:
+                    pszAxisName = SDL_GameControllerGetStringForAxis(SDL_CONTROLLER_AXIS_TRIGGERRIGHT);
+                    break;
+                }
+                SDL_strlcat(mapping, pszAxisName, SDL_arraysize(mapping));
             }
             SDL_strlcat(mapping, ":", SDL_arraysize(mapping));
 
@@ -450,7 +581,19 @@ WatchJoystick(SDL_Joystick * joystick)
                 SDL_snprintf(pszElement, sizeof(pszElement), "b%d", pBinding->value.button);
                 break;
             case SDL_CONTROLLER_BINDTYPE_AXIS:
-                SDL_snprintf(pszElement, sizeof(pszElement), "a%d", pBinding->value.axis);
+                if (pBinding->value.axis.axis_min == 0 && pBinding->value.axis.axis_max == SDL_JOYSTICK_AXIS_MIN) {
+                    /* The negative half axis */
+                    SDL_snprintf(pszElement, sizeof(pszElement), "-a%d", pBinding->value.axis.axis);
+                } else if (pBinding->value.axis.axis_min == 0 && pBinding->value.axis.axis_max == SDL_JOYSTICK_AXIS_MAX) {
+                    /* The positive half axis */
+                    SDL_snprintf(pszElement, sizeof(pszElement), "+a%d", pBinding->value.axis.axis);
+                } else {
+                    SDL_snprintf(pszElement, sizeof(pszElement), "a%d", pBinding->value.axis.axis);
+                    if (pBinding->value.axis.axis_min > pBinding->value.axis.axis_max) {
+                        /* Invert the axis */
+                        SDL_strlcat(pszElement, "~", SDL_arraysize(pszElement));
+                    }
+                }
                 break;
             case SDL_CONTROLLER_BINDTYPE_HAT:
                 SDL_snprintf(pszElement, sizeof(pszElement), "h%d.%d", pBinding->value.hat.hat, pBinding->value.hat.hat_mask);
@@ -467,6 +610,9 @@ WatchJoystick(SDL_Joystick * joystick)
         /* Print to stdout as well so the user can cat the output somewhere */
         printf("%s\n", mapping);
     }
+
+    SDL_free(s_arrAxisState);
+    s_arrAxisState = NULL;
     
     SDL_DestroyRenderer(screen);
     SDL_DestroyWindow(window);

+ 13 - 10
test/testgamecontroller.c

@@ -53,12 +53,12 @@ static const struct { int x; int y; } button_positions[] = {
 
 /* This is indexed by SDL_GameControllerAxis. */
 static const struct { int x; int y; double angle; } axis_positions[] = {
-    {75,  154, 0.0},  /* LEFTX */
-    {75,  154, 90.0},  /* LEFTY */
-    {305, 230, 0.0},  /* RIGHTX */
-    {305, 230, 90.0},  /* RIGHTY */
-    {91, 0, 90.0},     /* TRIGGERLEFT */
-    {375, 0, 90.0},    /* TRIGGERRIGHT */
+    {74,  153, 270.0},  /* LEFTX */
+    {74,  153, 0.0},  /* LEFTY */
+    {306, 231, 270.0},  /* RIGHTX */
+    {306, 231, 0.0},  /* RIGHTY */
+    {91, -20, 0.0},     /* TRIGGERLEFT */
+    {375, -20, 0.0},    /* TRIGGERRIGHT */
 };
 
 SDL_Renderer *screen = NULL;
@@ -80,10 +80,6 @@ LoadTexture(SDL_Renderer *renderer, const char *file, SDL_bool transparent)
         if (transparent) {
             if (temp->format->BytesPerPixel == 1) {
                 SDL_SetColorKey(temp, SDL_TRUE, *(Uint8 *)temp->pixels);
-            } else {
-                SDL_assert(!temp->format->palette);
-                SDL_assert(temp->format->BitsPerPixel == 24);
-                SDL_SetColorKey(temp, SDL_TRUE, (*(Uint32 *)temp->pixels) & 0x00FFFFFF);
             }
         }
 
@@ -112,6 +108,13 @@ loop(void *arg)
 
     while (SDL_PollEvent(&event)) {
         switch (event.type) {
+        case SDL_CONTROLLERAXISMOTION:
+            SDL_Log("Controller axis %s changed to %d\n", SDL_GameControllerGetStringForAxis(event.caxis.axis), event.caxis.value);
+            break;
+        case SDL_CONTROLLERBUTTONDOWN:
+        case SDL_CONTROLLERBUTTONUP:
+            SDL_Log("Controller button %s %s\n", SDL_GameControllerGetStringForButton(event.cbutton.button), event.cbutton.state ? "pressed" : "released");
+            break;
         case SDL_KEYDOWN:
             if (event.key.keysym.sym != SDLK_ESCAPE) {
                 break;