Split controller axes into positive and negative sides so each can be bound independently.
authorSam Lantinga <slouken@libsdl.org>
Tue, 27 Dec 2016 01:39:07 -0800
changeset 107245ea5f198879f
parent 10723 79be3e82dcf1
child 10725 fa8ffe60cf4f
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
include/SDL_joystick.h
src/joystick/SDL_gamecontroller.c
src/joystick/windows/SDL_dinputjoystick.c
src/joystick/windows/SDL_mmjoystick.c
test/axis.bmp
test/controllermap.c
test/testgamecontroller.c
     1.1 --- a/include/SDL_joystick.h	Mon Dec 26 23:02:14 2016 -0500
     1.2 +++ b/include/SDL_joystick.h	Tue Dec 27 01:39:07 2016 -0800
     1.3 @@ -232,6 +232,8 @@
     1.4   */
     1.5  extern DECLSPEC int SDLCALL SDL_JoystickEventState(int state);
     1.6  
     1.7 +#define SDL_JOYSTICK_AXIS_MAX   32767
     1.8 +#define SDL_JOYSTICK_AXIS_MIN   -32768
     1.9  /**
    1.10   *  Get the current state of an axis control on a joystick.
    1.11   *
     2.1 --- a/src/joystick/SDL_gamecontroller.c	Mon Dec 26 23:02:14 2016 -0500
     2.2 +++ b/src/joystick/SDL_gamecontroller.c	Tue Dec 27 01:39:07 2016 -0800
     2.3 @@ -32,53 +32,46 @@
     2.4  #if !SDL_EVENTS_DISABLED
     2.5  #include "../events/SDL_events_c.h"
     2.6  #endif
     2.7 -#define ABS(_x) ((_x) < 0 ? -(_x) : (_x))
     2.8  
     2.9  #define SDL_CONTROLLER_PLATFORM_FIELD "platform:"
    2.10  
    2.11  /* a list of currently opened game controllers */
    2.12  static SDL_GameController *SDL_gamecontrollers = NULL;
    2.13  
    2.14 -/* keep track of the hat and mask value that transforms this hat movement into a button/axis press */
    2.15 -struct _SDL_HatMapping
    2.16 +typedef struct
    2.17  {
    2.18 -    int hat;
    2.19 -    Uint8 mask;
    2.20 -};
    2.21 +    SDL_GameControllerBindType inputType;
    2.22 +    union
    2.23 +    {
    2.24 +        int button;
    2.25  
    2.26 -/* We need 36 entries for Android (as of SDL v2.0.4) */
    2.27 -#define k_nMaxReverseEntries 48
    2.28 +        struct {
    2.29 +            int axis;
    2.30 +            int axis_min;
    2.31 +            int axis_max;
    2.32 +        } axis;
    2.33  
    2.34 -/**
    2.35 - * We are encoding the "HAT" as 0xhm. where h == hat ID and m == mask
    2.36 - * MAX 4 hats supported
    2.37 - */
    2.38 -#define k_nMaxHatEntries 0x3f + 1
    2.39 +        struct {
    2.40 +            int hat;
    2.41 +            int hat_mask;
    2.42 +        } hat;
    2.43  
    2.44 -/* our in memory mapping db between joystick objects and controller mappings */
    2.45 -struct _SDL_ControllerMapping
    2.46 -{
    2.47 -    SDL_JoystickGUID guid;
    2.48 -    const char *name;
    2.49 +    } input;
    2.50  
    2.51 -    /* mapping of axis/button id to controller version */
    2.52 -    int axes[SDL_CONTROLLER_AXIS_MAX];
    2.53 -    int buttonasaxis[SDL_CONTROLLER_AXIS_MAX];
    2.54 +    SDL_GameControllerBindType outputType;
    2.55 +    union
    2.56 +    {
    2.57 +        SDL_GameControllerButton button;
    2.58  
    2.59 -    int buttons[SDL_CONTROLLER_BUTTON_MAX];
    2.60 -    int axesasbutton[SDL_CONTROLLER_BUTTON_MAX];
    2.61 -    struct _SDL_HatMapping hatasbutton[SDL_CONTROLLER_BUTTON_MAX];
    2.62 +        struct {
    2.63 +            SDL_GameControllerAxis axis;
    2.64 +            int axis_min;
    2.65 +            int axis_max;
    2.66 +        } axis;
    2.67  
    2.68 -    /* reverse mapping, joystick indices to buttons */
    2.69 -    SDL_GameControllerAxis raxes[k_nMaxReverseEntries];
    2.70 -    SDL_GameControllerAxis rbuttonasaxis[k_nMaxReverseEntries];
    2.71 +    } output;
    2.72  
    2.73 -    SDL_GameControllerButton rbuttons[k_nMaxReverseEntries];
    2.74 -    SDL_GameControllerButton raxesasbutton[k_nMaxReverseEntries];
    2.75 -    SDL_GameControllerButton rhatasbutton[k_nMaxHatEntries];
    2.76 -
    2.77 -};
    2.78 -
    2.79 +} SDL_ExtendedGameControllerBind;
    2.80  
    2.81  /* our hard coded list of mapping support */
    2.82  typedef enum
    2.83 @@ -107,14 +100,20 @@
    2.84  {
    2.85      SDL_Joystick *joystick; /* underlying joystick device */
    2.86      int ref_count;
    2.87 -    Uint8 hatState[4]; /* the current hat state for this controller */
    2.88 -    struct _SDL_ControllerMapping mapping; /* the mapping object for this controller */
    2.89 +
    2.90 +    SDL_JoystickGUID guid;
    2.91 +    const char *name;
    2.92 +    int num_bindings;
    2.93 +    SDL_ExtendedGameControllerBind *bindings;
    2.94 +    SDL_ExtendedGameControllerBind **last_match_axis;
    2.95 +    Uint8 *last_hat_mask;
    2.96 +
    2.97      struct _SDL_GameController *next; /* pointer to next game controller we have allocated */
    2.98  };
    2.99  
   2.100  
   2.101 -int SDL_PrivateGameControllerAxis(SDL_GameController * gamecontroller, SDL_GameControllerAxis axis, Sint16 value);
   2.102 -int SDL_PrivateGameControllerButton(SDL_GameController * gamecontroller, SDL_GameControllerButton button, Uint8 state);
   2.103 +static int SDL_PrivateGameControllerAxis(SDL_GameController * gamecontroller, SDL_GameControllerAxis axis, Sint16 value);
   2.104 +static int SDL_PrivateGameControllerButton(SDL_GameController * gamecontroller, SDL_GameControllerButton button, Uint8 state);
   2.105  
   2.106  /*
   2.107   * If there is an existing add event in the queue, it needs to be modified
   2.108 @@ -145,6 +144,124 @@
   2.109      SDL_stack_free(events);
   2.110  }
   2.111  
   2.112 +static SDL_bool HasSameOutput(SDL_ExtendedGameControllerBind *a, SDL_ExtendedGameControllerBind *b)
   2.113 +{
   2.114 +    if (a->outputType != b->outputType) {
   2.115 +        return SDL_FALSE;
   2.116 +    }
   2.117 +
   2.118 +    if (a->outputType == SDL_CONTROLLER_BINDTYPE_AXIS) {
   2.119 +        return (a->output.axis.axis == b->output.axis.axis);
   2.120 +    } else {
   2.121 +        return (a->output.button == b->output.button);
   2.122 +    }
   2.123 +}
   2.124 +
   2.125 +static void ResetOutput(SDL_GameController *gamecontroller, SDL_ExtendedGameControllerBind *bind)
   2.126 +{
   2.127 +    if (bind->outputType == SDL_CONTROLLER_BINDTYPE_AXIS) {
   2.128 +        SDL_PrivateGameControllerAxis(gamecontroller, bind->output.axis.axis, 0);
   2.129 +    } else {
   2.130 +        SDL_PrivateGameControllerButton(gamecontroller, bind->output.button, SDL_RELEASED);
   2.131 +    }
   2.132 +}
   2.133 +
   2.134 +static void HandleJoystickAxis(SDL_GameController *gamecontroller, int axis, int value)
   2.135 +{
   2.136 +    int i;
   2.137 +    SDL_ExtendedGameControllerBind *last_match = gamecontroller->last_match_axis[axis];
   2.138 +    SDL_ExtendedGameControllerBind *match = NULL;
   2.139 +
   2.140 +    for (i = 0; i < gamecontroller->num_bindings; ++i) {
   2.141 +        SDL_ExtendedGameControllerBind *binding = &gamecontroller->bindings[i];
   2.142 +        if (binding->inputType == SDL_CONTROLLER_BINDTYPE_AXIS &&
   2.143 +            axis == binding->input.axis.axis) {
   2.144 +            if (binding->input.axis.axis_min < binding->input.axis.axis_max) {
   2.145 +                if (value >= binding->input.axis.axis_min &&
   2.146 +                    value <= binding->input.axis.axis_max) {
   2.147 +                    match = binding;
   2.148 +                    break;
   2.149 +                }
   2.150 +            } else {
   2.151 +                if (value >= binding->input.axis.axis_max &&
   2.152 +                    value <= binding->input.axis.axis_min) {
   2.153 +                    match = binding;
   2.154 +                    break;
   2.155 +                }
   2.156 +            }
   2.157 +        }
   2.158 +    }
   2.159 +
   2.160 +    if (last_match && (!match || !HasSameOutput(last_match, match))) {
   2.161 +        /* Clear the last input that this axis generated */
   2.162 +        ResetOutput(gamecontroller, last_match);
   2.163 +    }
   2.164 +
   2.165 +    if (match) {
   2.166 +        if (match->outputType == SDL_CONTROLLER_BINDTYPE_AXIS) {
   2.167 +            if (match->input.axis.axis_min != match->output.axis.axis_min || match->input.axis.axis_max != match->output.axis.axis_max) {
   2.168 +                float normalized_value = (float)(value - match->input.axis.axis_min) / (match->input.axis.axis_max - match->input.axis.axis_min);
   2.169 +                value = match->output.axis.axis_min + (int)(normalized_value * (match->output.axis.axis_max - match->output.axis.axis_min));
   2.170 +            }
   2.171 +            SDL_PrivateGameControllerAxis(gamecontroller, match->output.axis.axis, (Sint16)value);
   2.172 +        } else {
   2.173 +            Uint8 state;
   2.174 +            int threshold = match->input.axis.axis_min + (match->input.axis.axis_max - match->input.axis.axis_min) / 2;
   2.175 +            if (match->input.axis.axis_max < match->input.axis.axis_min) {
   2.176 +                state = (value <= threshold) ? SDL_PRESSED : SDL_RELEASED;
   2.177 +            } else {
   2.178 +                state = (value >= threshold) ? SDL_PRESSED : SDL_RELEASED;
   2.179 +            }
   2.180 +            SDL_PrivateGameControllerButton(gamecontroller, match->output.button, state);
   2.181 +        }
   2.182 +    }
   2.183 +    gamecontroller->last_match_axis[axis] = match;
   2.184 +}
   2.185 +
   2.186 +static void HandleJoystickButton(SDL_GameController *gamecontroller, int button, Uint8 state)
   2.187 +{
   2.188 +    int i;
   2.189 +
   2.190 +    for (i = 0; i < gamecontroller->num_bindings; ++i) {
   2.191 +        SDL_ExtendedGameControllerBind *binding = &gamecontroller->bindings[i];
   2.192 +        if (binding->inputType == SDL_CONTROLLER_BINDTYPE_BUTTON &&
   2.193 +            button == binding->input.button) {
   2.194 +            if (binding->outputType == SDL_CONTROLLER_BINDTYPE_AXIS) {
   2.195 +                int value = state ? binding->output.axis.axis_max : binding->output.axis.axis_min;
   2.196 +                SDL_PrivateGameControllerAxis(gamecontroller, binding->output.axis.axis, (Sint16)value);
   2.197 +            } else {
   2.198 +                SDL_PrivateGameControllerButton(gamecontroller, binding->output.button, state);
   2.199 +            }
   2.200 +            break;
   2.201 +        }
   2.202 +    }
   2.203 +}
   2.204 +
   2.205 +static void HandleJoystickHat(SDL_GameController *gamecontroller, int hat, Uint8 value)
   2.206 +{
   2.207 +    int i;
   2.208 +    Uint8 last_mask = gamecontroller->last_hat_mask[hat];
   2.209 +    Uint8 changed_mask = (last_mask ^ value);
   2.210 +
   2.211 +    for (i = 0; i < gamecontroller->num_bindings; ++i) {
   2.212 +        SDL_ExtendedGameControllerBind *binding = &gamecontroller->bindings[i];
   2.213 +        if (binding->inputType == SDL_CONTROLLER_BINDTYPE_HAT && hat == binding->input.hat.hat) {
   2.214 +            if ((changed_mask & binding->input.hat.hat_mask) != 0) {
   2.215 +                if (value & binding->input.hat.hat_mask) {
   2.216 +                    if (binding->outputType == SDL_CONTROLLER_BINDTYPE_AXIS) {
   2.217 +                        SDL_PrivateGameControllerAxis(gamecontroller, binding->output.axis.axis, (Sint16)binding->output.axis.axis_max);
   2.218 +                    } else {
   2.219 +                        SDL_PrivateGameControllerButton(gamecontroller, binding->output.button, SDL_PRESSED);
   2.220 +                    }
   2.221 +                } else {
   2.222 +                    ResetOutput(gamecontroller, binding);
   2.223 +                }
   2.224 +            }
   2.225 +        }
   2.226 +    }
   2.227 +    gamecontroller->last_hat_mask[hat] = value;
   2.228 +}
   2.229 +
   2.230  /*
   2.231   * Event filter to fire controller events from joystick ones
   2.232   */
   2.233 @@ -153,32 +270,10 @@
   2.234      switch(event->type) {
   2.235      case SDL_JOYAXISMOTION:
   2.236          {
   2.237 -            SDL_GameController *controllerlist;
   2.238 -
   2.239 -            if (event->jaxis.axis >= k_nMaxReverseEntries)
   2.240 -            {
   2.241 -                SDL_SetError("SDL_GameControllerEventWatcher: Axis index %d too large, ignoring motion", (int)event->jaxis.axis);
   2.242 -                break;
   2.243 -            }
   2.244 -
   2.245 -            controllerlist = SDL_gamecontrollers;
   2.246 +            SDL_GameController *controllerlist = SDL_gamecontrollers;
   2.247              while (controllerlist) {
   2.248                  if (controllerlist->joystick->instance_id == event->jaxis.which) {
   2.249 -                    if (controllerlist->mapping.raxes[event->jaxis.axis] >= 0) /* simple axis to axis, send it through */ {
   2.250 -                        SDL_GameControllerAxis axis = controllerlist->mapping.raxes[event->jaxis.axis];
   2.251 -                        Sint16 value = event->jaxis.value;
   2.252 -                        switch (axis) {
   2.253 -                            case SDL_CONTROLLER_AXIS_TRIGGERLEFT:
   2.254 -                            case SDL_CONTROLLER_AXIS_TRIGGERRIGHT:
   2.255 -                                value = value / 2 + 16384;
   2.256 -                                break;
   2.257 -                            default:
   2.258 -                                break;
   2.259 -                        }
   2.260 -                        SDL_PrivateGameControllerAxis(controllerlist, axis, value);
   2.261 -                    } else if (controllerlist->mapping.raxesasbutton[event->jaxis.axis] >= 0) { /* simulate an axis as a button */
   2.262 -                        SDL_PrivateGameControllerButton(controllerlist, controllerlist->mapping.raxesasbutton[event->jaxis.axis], ABS(event->jaxis.value) > 32768/2 ? SDL_PRESSED : SDL_RELEASED);
   2.263 -                    }
   2.264 +                    HandleJoystickAxis(controllerlist, event->jaxis.axis, event->jaxis.value);
   2.265                      break;
   2.266                  }
   2.267                  controllerlist = controllerlist->next;
   2.268 @@ -188,22 +283,10 @@
   2.269      case SDL_JOYBUTTONDOWN:
   2.270      case SDL_JOYBUTTONUP:
   2.271          {
   2.272 -            SDL_GameController *controllerlist;
   2.273 -
   2.274 -            if (event->jbutton.button >= k_nMaxReverseEntries)
   2.275 -            {
   2.276 -                SDL_SetError("SDL_GameControllerEventWatcher: Button index %d too large, ignoring update", (int)event->jbutton.button);
   2.277 -                break;
   2.278 -            }
   2.279 -
   2.280 -            controllerlist = SDL_gamecontrollers;
   2.281 +            SDL_GameController *controllerlist = SDL_gamecontrollers;
   2.282              while (controllerlist) {
   2.283                  if (controllerlist->joystick->instance_id == event->jbutton.which) {
   2.284 -                    if (controllerlist->mapping.rbuttons[event->jbutton.button] >= 0) { /* simple button as button */
   2.285 -                        SDL_PrivateGameControllerButton(controllerlist, controllerlist->mapping.rbuttons[event->jbutton.button], event->jbutton.state);
   2.286 -                    } else if (controllerlist->mapping.rbuttonasaxis[event->jbutton.button] >= 0) { /* an button pretending to be an axis */
   2.287 -                        SDL_PrivateGameControllerAxis(controllerlist, controllerlist->mapping.rbuttonasaxis[event->jbutton.button], event->jbutton.state > 0 ? 32767 : 0);
   2.288 -                    }
   2.289 +                    HandleJoystickButton(controllerlist, event->jbutton.button, event->jbutton.state);
   2.290                      break;
   2.291                  }
   2.292                  controllerlist = controllerlist->next;
   2.293 @@ -212,43 +295,10 @@
   2.294          break;
   2.295      case SDL_JOYHATMOTION:
   2.296          {
   2.297 -            SDL_GameController *controllerlist;
   2.298 -
   2.299 -            if (event->jhat.hat >= 4) break;
   2.300 -
   2.301 -            controllerlist = SDL_gamecontrollers;
   2.302 +            SDL_GameController *controllerlist = SDL_gamecontrollers;
   2.303              while (controllerlist) {
   2.304                  if (controllerlist->joystick->instance_id == event->jhat.which) {
   2.305 -                    Uint8 bSame = controllerlist->hatState[event->jhat.hat] & event->jhat.value;
   2.306 -                    /* Get list of removed bits (button release) */
   2.307 -                    Uint8 bChanged = controllerlist->hatState[event->jhat.hat] ^ bSame;
   2.308 -                    /* the hat idx in the high nibble */
   2.309 -                    int bHighHat = event->jhat.hat << 4;
   2.310 -
   2.311 -                    if (bChanged & SDL_HAT_DOWN)
   2.312 -                        SDL_PrivateGameControllerButton(controllerlist, controllerlist->mapping.rhatasbutton[bHighHat | SDL_HAT_DOWN], SDL_RELEASED);
   2.313 -                    if (bChanged & SDL_HAT_UP)
   2.314 -                        SDL_PrivateGameControllerButton(controllerlist, controllerlist->mapping.rhatasbutton[bHighHat | SDL_HAT_UP], SDL_RELEASED);
   2.315 -                    if (bChanged & SDL_HAT_LEFT)
   2.316 -                        SDL_PrivateGameControllerButton(controllerlist, controllerlist->mapping.rhatasbutton[bHighHat | SDL_HAT_LEFT], SDL_RELEASED);
   2.317 -                    if (bChanged & SDL_HAT_RIGHT)
   2.318 -                        SDL_PrivateGameControllerButton(controllerlist, controllerlist->mapping.rhatasbutton[bHighHat | SDL_HAT_RIGHT], SDL_RELEASED);
   2.319 -
   2.320 -                    /* Get list of added bits (button press) */
   2.321 -                    bChanged = event->jhat.value ^ bSame;
   2.322 -
   2.323 -                    if (bChanged & SDL_HAT_DOWN)
   2.324 -                        SDL_PrivateGameControllerButton(controllerlist, controllerlist->mapping.rhatasbutton[bHighHat | SDL_HAT_DOWN], SDL_PRESSED);
   2.325 -                    if (bChanged & SDL_HAT_UP)
   2.326 -                        SDL_PrivateGameControllerButton(controllerlist, controllerlist->mapping.rhatasbutton[bHighHat | SDL_HAT_UP], SDL_PRESSED);
   2.327 -                    if (bChanged & SDL_HAT_LEFT)
   2.328 -                        SDL_PrivateGameControllerButton(controllerlist, controllerlist->mapping.rhatasbutton[bHighHat | SDL_HAT_LEFT], SDL_PRESSED);
   2.329 -                    if (bChanged & SDL_HAT_RIGHT)
   2.330 -                        SDL_PrivateGameControllerButton(controllerlist, controllerlist->mapping.rhatasbutton[bHighHat | SDL_HAT_RIGHT], SDL_PRESSED);
   2.331 -
   2.332 -                    /* update our state cache */
   2.333 -                    controllerlist->hatState[event->jhat.hat] = event->jhat.value;
   2.334 -
   2.335 +                    HandleJoystickHat(controllerlist, event->jhat.hat, event->jhat.value);
   2.336                      break;
   2.337                  }
   2.338                  controllerlist = controllerlist->next;
   2.339 @@ -321,8 +371,14 @@
   2.340  SDL_GameControllerAxis SDL_GameControllerGetAxisFromString(const char *pchString)
   2.341  {
   2.342      int entry;
   2.343 -    if (!pchString || !pchString[0])
   2.344 +
   2.345 +    if (pchString && (*pchString == '+' || *pchString == '-')) {
   2.346 +        ++pchString;
   2.347 +    }
   2.348 +
   2.349 +    if (!pchString || !pchString[0]) {
   2.350          return SDL_CONTROLLER_AXIS_INVALID;
   2.351 +    }
   2.352  
   2.353      for (entry = 0; map_StringForControllerAxis[entry]; ++entry) {
   2.354          if (!SDL_strcasecmp(pchString, map_StringForControllerAxis[entry]))
   2.355 @@ -391,63 +447,95 @@
   2.356  /*
   2.357   * given a controller button name and a joystick name update our mapping structure with it
   2.358   */
   2.359 -static void SDL_PrivateGameControllerParseButton(const char *szGameButton, const char *szJoystickButton, struct _SDL_ControllerMapping *pMapping)
   2.360 +static void SDL_PrivateGameControllerParseElement(SDL_GameController *gamecontroller, const char *szGameButton, const char *szJoystickButton)
   2.361  {
   2.362 -    int iSDLButton = 0;
   2.363 +    SDL_ExtendedGameControllerBind bind;
   2.364      SDL_GameControllerButton button;
   2.365      SDL_GameControllerAxis axis;
   2.366 +    SDL_bool invert_input = SDL_FALSE;
   2.367 +    char half_axis_input = 0;
   2.368 +    char half_axis_output = 0;
   2.369 +
   2.370 +    if (*szGameButton == '+' || *szGameButton == '-') {
   2.371 +        half_axis_output = *szGameButton++;
   2.372 +    }
   2.373 +
   2.374 +    axis = SDL_GameControllerGetAxisFromString(szGameButton);
   2.375      button = SDL_GameControllerGetButtonFromString(szGameButton);
   2.376 -    axis = SDL_GameControllerGetAxisFromString(szGameButton);
   2.377 -    iSDLButton = SDL_atoi(&szJoystickButton[1]);
   2.378 +    if (axis != SDL_CONTROLLER_AXIS_INVALID) {
   2.379 +        bind.outputType = SDL_CONTROLLER_BINDTYPE_AXIS;
   2.380 +        bind.output.axis.axis = axis;
   2.381 +        if (axis == SDL_CONTROLLER_AXIS_TRIGGERLEFT || axis == SDL_CONTROLLER_AXIS_TRIGGERRIGHT) {
   2.382 +            bind.output.axis.axis_min = 0;
   2.383 +            bind.output.axis.axis_max = SDL_JOYSTICK_AXIS_MAX;
   2.384 +        } else {
   2.385 +            if (half_axis_output == '+') {
   2.386 +                bind.output.axis.axis_min = 0;
   2.387 +                bind.output.axis.axis_max = SDL_JOYSTICK_AXIS_MAX;
   2.388 +            } else if (half_axis_output == '-') {
   2.389 +                bind.output.axis.axis_min = 0;
   2.390 +                bind.output.axis.axis_max = SDL_JOYSTICK_AXIS_MIN;
   2.391 +            } else {
   2.392 +                bind.output.axis.axis_min = SDL_JOYSTICK_AXIS_MIN;
   2.393 +                bind.output.axis.axis_max = SDL_JOYSTICK_AXIS_MAX;
   2.394 +            }
   2.395 +        }
   2.396 +    } else if (button != SDL_CONTROLLER_BUTTON_INVALID) {
   2.397 +        bind.outputType = SDL_CONTROLLER_BINDTYPE_BUTTON;
   2.398 +        bind.output.button = button;
   2.399 +    } else {
   2.400 +        SDL_SetError("Unexpected controller element %s", szGameButton);
   2.401 +        return;
   2.402 +    }
   2.403  
   2.404 -    if (szJoystickButton[0] == 'a') {
   2.405 -        if (iSDLButton >= k_nMaxReverseEntries) {
   2.406 -            SDL_SetError("Axis index too large: %d", iSDLButton);
   2.407 -            return;
   2.408 +    if (*szJoystickButton == '+' || *szJoystickButton == '-') {
   2.409 +        half_axis_input = *szJoystickButton++;
   2.410 +    }
   2.411 +    if (szJoystickButton[SDL_strlen(szJoystickButton) - 1] == '~') {
   2.412 +        invert_input = SDL_TRUE;
   2.413 +    }
   2.414 +
   2.415 +    if (szJoystickButton[0] == 'a' && SDL_isdigit(szJoystickButton[1])) {
   2.416 +        bind.inputType = SDL_CONTROLLER_BINDTYPE_AXIS;
   2.417 +        bind.input.axis.axis = SDL_atoi(&szJoystickButton[1]);
   2.418 +        if (half_axis_input == '+') {
   2.419 +            bind.input.axis.axis_min = 0;
   2.420 +            bind.input.axis.axis_max = SDL_JOYSTICK_AXIS_MAX;
   2.421 +        } else if (half_axis_input == '-') {
   2.422 +            bind.input.axis.axis_min = 0;
   2.423 +            bind.input.axis.axis_max = SDL_JOYSTICK_AXIS_MIN;
   2.424 +        } else {
   2.425 +            bind.input.axis.axis_min = SDL_JOYSTICK_AXIS_MIN;
   2.426 +            bind.input.axis.axis_max = SDL_JOYSTICK_AXIS_MAX;
   2.427          }
   2.428 -        if (axis != SDL_CONTROLLER_AXIS_INVALID) {
   2.429 -            pMapping->axes[axis] = iSDLButton;
   2.430 -            pMapping->raxes[iSDLButton] = axis;
   2.431 -        } else if (button != SDL_CONTROLLER_BUTTON_INVALID) {
   2.432 -            pMapping->axesasbutton[button] = iSDLButton;
   2.433 -            pMapping->raxesasbutton[iSDLButton] = button;
   2.434 -        } else {
   2.435 -            SDL_assert(!"How did we get here?");
   2.436 +        if (invert_input) {
   2.437 +            int tmp = bind.input.axis.axis_min;
   2.438 +            bind.input.axis.axis_min = bind.input.axis.axis_max;
   2.439 +            bind.input.axis.axis_max = tmp;
   2.440          }
   2.441 -
   2.442 -    } else if (szJoystickButton[0] == 'b') {
   2.443 -        if (iSDLButton >= k_nMaxReverseEntries) {
   2.444 -            SDL_SetError("Button index too large: %d", iSDLButton);
   2.445 -            return;
   2.446 -        }
   2.447 -        if (button != SDL_CONTROLLER_BUTTON_INVALID) {
   2.448 -            pMapping->buttons[button] = iSDLButton;
   2.449 -            pMapping->rbuttons[iSDLButton] = button;
   2.450 -        } else if (axis != SDL_CONTROLLER_AXIS_INVALID) {
   2.451 -            pMapping->buttonasaxis[axis] = iSDLButton;
   2.452 -            pMapping->rbuttonasaxis[iSDLButton] = axis;
   2.453 -        } else {
   2.454 -            SDL_assert(!"How did we get here?");
   2.455 -        }
   2.456 -    } else if (szJoystickButton[0] == 'h') {
   2.457 +    } else if (szJoystickButton[0] == 'b' && SDL_isdigit(szJoystickButton[1])) {
   2.458 +        bind.inputType = SDL_CONTROLLER_BINDTYPE_BUTTON;
   2.459 +        bind.input.button = SDL_atoi(&szJoystickButton[1]);
   2.460 +    } else if (szJoystickButton[0] == 'h' && SDL_isdigit(szJoystickButton[1]) &&
   2.461 +               szJoystickButton[2] == '.' && SDL_isdigit(szJoystickButton[3])) {
   2.462          int hat = SDL_atoi(&szJoystickButton[1]);
   2.463          int mask = SDL_atoi(&szJoystickButton[3]);
   2.464 -        if (hat >= 4) {
   2.465 -            SDL_SetError("Hat index too large: %d", iSDLButton);
   2.466 -        }
   2.467 +        bind.inputType = SDL_CONTROLLER_BINDTYPE_HAT;
   2.468 +        bind.input.hat.hat = hat;
   2.469 +        bind.input.hat.hat_mask = mask;
   2.470 +    } else {
   2.471 +        SDL_SetError("Unexpected joystick element: %s", szJoystickButton);
   2.472 +        return;
   2.473 +    }
   2.474  
   2.475 -        if (button != SDL_CONTROLLER_BUTTON_INVALID) {
   2.476 -            int ridx;
   2.477 -            pMapping->hatasbutton[button].hat = hat;
   2.478 -            pMapping->hatasbutton[button].mask = mask;
   2.479 -            ridx = (hat << 4) | mask;
   2.480 -            pMapping->rhatasbutton[ridx] = button;
   2.481 -        } else if (axis != SDL_CONTROLLER_AXIS_INVALID) {
   2.482 -            SDL_assert(!"Support hat as axis");
   2.483 -        } else {
   2.484 -            SDL_assert(!"How did we get here?");
   2.485 -        }
   2.486 +    ++gamecontroller->num_bindings;
   2.487 +    gamecontroller->bindings = (SDL_ExtendedGameControllerBind *)SDL_realloc(gamecontroller->bindings, gamecontroller->num_bindings * sizeof(*gamecontroller->bindings));
   2.488 +    if (!gamecontroller->bindings) {
   2.489 +        gamecontroller->num_bindings = 0;
   2.490 +        SDL_OutOfMemory();
   2.491 +        return;
   2.492      }
   2.493 +    gamecontroller->bindings[gamecontroller->num_bindings - 1] = bind;
   2.494  }
   2.495  
   2.496  
   2.497 @@ -455,7 +543,7 @@
   2.498   * given a controller mapping string update our mapping object
   2.499   */
   2.500  static void
   2.501 -SDL_PrivateGameControllerParseControllerConfigString(struct _SDL_ControllerMapping *pMapping, const char *pchString)
   2.502 +SDL_PrivateGameControllerParseControllerConfigString(SDL_GameController *gamecontroller, const char *pchString)
   2.503  {
   2.504      char szGameButton[20];
   2.505      char szJoystickButton[20];
   2.506 @@ -463,8 +551,8 @@
   2.507      int i = 0;
   2.508      const char *pchPos = pchString;
   2.509  
   2.510 -    SDL_memset(szGameButton, 0x0, sizeof(szGameButton));
   2.511 -    SDL_memset(szJoystickButton, 0x0, sizeof(szJoystickButton));
   2.512 +    SDL_zero(szGameButton);
   2.513 +    SDL_zero(szJoystickButton);
   2.514  
   2.515      while (pchPos && *pchPos) {
   2.516          if (*pchPos == ':') {
   2.517 @@ -475,9 +563,9 @@
   2.518          } else if (*pchPos == ',') {
   2.519              i = 0;
   2.520              bGameButton = SDL_TRUE;
   2.521 -            SDL_PrivateGameControllerParseButton(szGameButton, szJoystickButton, pMapping);
   2.522 -            SDL_memset(szGameButton, 0x0, sizeof(szGameButton));
   2.523 -            SDL_memset(szJoystickButton, 0x0, sizeof(szJoystickButton));
   2.524 +            SDL_PrivateGameControllerParseElement(gamecontroller, szGameButton, szJoystickButton);
   2.525 +            SDL_zero(szGameButton);
   2.526 +            SDL_zero(szJoystickButton);
   2.527  
   2.528          } else if (bGameButton) {
   2.529              if (i >= sizeof(szGameButton)) {
   2.530 @@ -497,43 +585,37 @@
   2.531          pchPos++;
   2.532      }
   2.533  
   2.534 -    SDL_PrivateGameControllerParseButton(szGameButton, szJoystickButton, pMapping);
   2.535 +    SDL_PrivateGameControllerParseElement(gamecontroller, szGameButton, szJoystickButton);
   2.536  
   2.537  }
   2.538  
   2.539  /*
   2.540   * Make a new button mapping struct
   2.541   */
   2.542 -static void SDL_PrivateLoadButtonMapping(struct _SDL_ControllerMapping *pMapping, SDL_JoystickGUID guid, const char *pchName, const char *pchMapping)
   2.543 +static void SDL_PrivateLoadButtonMapping(SDL_GameController *gamecontroller, SDL_JoystickGUID guid, const char *pchName, const char *pchMapping)
   2.544  {
   2.545 -    int j;
   2.546 +    int i;
   2.547  
   2.548 -    pMapping->guid = guid;
   2.549 -    pMapping->name = pchName;
   2.550 +    gamecontroller->guid = guid;
   2.551 +    gamecontroller->name = pchName;
   2.552 +    gamecontroller->num_bindings = 0;
   2.553 +    SDL_memset(gamecontroller->last_match_axis, 0, gamecontroller->joystick->naxes * sizeof(*gamecontroller->last_match_axis));
   2.554  
   2.555 -    /* set all the button mappings to non defaults */
   2.556 -    for (j = 0; j < SDL_CONTROLLER_AXIS_MAX; j++) {
   2.557 -        pMapping->axes[j] = -1;
   2.558 -        pMapping->buttonasaxis[j] = -1;
   2.559 +    SDL_PrivateGameControllerParseControllerConfigString(gamecontroller, pchMapping);
   2.560 +
   2.561 +    /* Set the zero point for triggers */
   2.562 +    for (i = 0; i < gamecontroller->num_bindings; ++i) {
   2.563 +        SDL_ExtendedGameControllerBind *binding = &gamecontroller->bindings[i];
   2.564 +        if (binding->inputType == SDL_CONTROLLER_BINDTYPE_AXIS &&
   2.565 +            binding->outputType == SDL_CONTROLLER_BINDTYPE_AXIS &&
   2.566 +            (binding->output.axis.axis == SDL_CONTROLLER_AXIS_TRIGGERLEFT ||
   2.567 +             binding->output.axis.axis == SDL_CONTROLLER_AXIS_TRIGGERRIGHT)) {
   2.568 +            if (binding->input.axis.axis < gamecontroller->joystick->naxes) {
   2.569 +                gamecontroller->joystick->axes[binding->input.axis.axis].value =
   2.570 +                gamecontroller->joystick->axes[binding->input.axis.axis].zero = (Sint16)binding->input.axis.axis_min;
   2.571 +            }
   2.572 +        }
   2.573      }
   2.574 -    for (j = 0; j < SDL_CONTROLLER_BUTTON_MAX; j++) {
   2.575 -        pMapping->buttons[j] = -1;
   2.576 -        pMapping->axesasbutton[j] = -1;
   2.577 -        pMapping->hatasbutton[j].hat = -1;
   2.578 -    }
   2.579 -
   2.580 -    for (j = 0; j < k_nMaxReverseEntries; j++) {
   2.581 -        pMapping->raxes[j] = SDL_CONTROLLER_AXIS_INVALID;
   2.582 -        pMapping->rbuttonasaxis[j] = SDL_CONTROLLER_AXIS_INVALID;
   2.583 -        pMapping->rbuttons[j] = SDL_CONTROLLER_BUTTON_INVALID;
   2.584 -        pMapping->raxesasbutton[j] = SDL_CONTROLLER_BUTTON_INVALID;
   2.585 -    }
   2.586 -
   2.587 -    for (j = 0; j < k_nMaxHatEntries; j++) {
   2.588 -        pMapping->rhatasbutton[j] = SDL_CONTROLLER_BUTTON_INVALID;
   2.589 -    }
   2.590 -
   2.591 -    SDL_PrivateGameControllerParseControllerConfigString(pMapping, pchMapping);
   2.592  }
   2.593  
   2.594  
   2.595 @@ -628,14 +710,14 @@
   2.596  {
   2.597      SDL_GameController *gamecontrollerlist = SDL_gamecontrollers;
   2.598      while (gamecontrollerlist) {
   2.599 -        if (!SDL_memcmp(&gamecontrollerlist->mapping.guid, &pControllerMapping->guid, sizeof(pControllerMapping->guid))) {
   2.600 +        if (!SDL_memcmp(&gamecontrollerlist->guid, &pControllerMapping->guid, sizeof(pControllerMapping->guid))) {
   2.601              SDL_Event event;
   2.602              event.type = SDL_CONTROLLERDEVICEREMAPPED;
   2.603              event.cdevice.which = gamecontrollerlist->joystick->instance_id;
   2.604              SDL_PushEvent(&event);
   2.605  
   2.606              /* Not really threadsafe.  Should this lock access within SDL_GameControllerEventWatcher? */
   2.607 -            SDL_PrivateLoadButtonMapping(&gamecontrollerlist->mapping, pControllerMapping->guid, pControllerMapping->name, pControllerMapping->mapping);
   2.608 +            SDL_PrivateLoadButtonMapping(gamecontrollerlist, pControllerMapping->guid, pControllerMapping->name, pControllerMapping->mapping);
   2.609          }
   2.610  
   2.611          gamecontrollerlist = gamecontrollerlist->next;
   2.612 @@ -972,7 +1054,7 @@
   2.613          return NULL;
   2.614      }
   2.615  
   2.616 -    return SDL_GameControllerMappingForGUID(gamecontroller->mapping.guid);
   2.617 +    return SDL_GameControllerMappingForGUID(gamecontroller->guid);
   2.618  }
   2.619  
   2.620  static void
   2.621 @@ -1046,7 +1128,7 @@
   2.622  const char *
   2.623  SDL_GameControllerNameForIndex(int device_index)
   2.624  {
   2.625 -    ControllerMapping_t *pSupportedController =  SDL_PrivateGetControllerMapping(device_index);
   2.626 +    ControllerMapping_t *pSupportedController = SDL_PrivateGetControllerMapping(device_index);
   2.627      if (pSupportedController) {
   2.628          return pSupportedController->name;
   2.629      }
   2.630 @@ -1060,11 +1142,10 @@
   2.631  SDL_bool
   2.632  SDL_IsGameController(int device_index)
   2.633  {
   2.634 -    ControllerMapping_t *pSupportedController =  SDL_PrivateGetControllerMapping(device_index);
   2.635 +    ControllerMapping_t *pSupportedController = SDL_PrivateGetControllerMapping(device_index);
   2.636      if (pSupportedController) {
   2.637          return SDL_TRUE;
   2.638      }
   2.639 -
   2.640      return SDL_FALSE;
   2.641  }
   2.642  
   2.643 @@ -1110,14 +1191,13 @@
   2.644      }
   2.645  
   2.646      /* Create and initialize the joystick */
   2.647 -    gamecontroller = (SDL_GameController *) SDL_malloc((sizeof *gamecontroller));
   2.648 +    gamecontroller = (SDL_GameController *) SDL_calloc(1, sizeof(*gamecontroller));
   2.649      if (gamecontroller == NULL) {
   2.650          SDL_OutOfMemory();
   2.651          SDL_UnlockJoystickList();
   2.652          return NULL;
   2.653      }
   2.654  
   2.655 -    SDL_memset(gamecontroller, 0, (sizeof *gamecontroller));
   2.656      gamecontroller->joystick = SDL_JoystickOpen(device_index);
   2.657      if (!gamecontroller->joystick) {
   2.658          SDL_free(gamecontroller);
   2.659 @@ -1125,21 +1205,10 @@
   2.660          return NULL;
   2.661      }
   2.662  
   2.663 -    SDL_PrivateLoadButtonMapping(&gamecontroller->mapping, pSupportedController->guid, pSupportedController->name, pSupportedController->mapping);
   2.664 +    gamecontroller->last_match_axis = (SDL_ExtendedGameControllerBind **)SDL_calloc(gamecontroller->joystick->naxes, sizeof(*gamecontroller->last_match_axis));
   2.665 +    gamecontroller->last_hat_mask = (Uint8 *)SDL_calloc(gamecontroller->joystick->nhats, sizeof(*gamecontroller->last_hat_mask));
   2.666  
   2.667 -    /* The triggers are mapped from -32768 to 32767, where -32768 is the 'unpressed' value */
   2.668 -    {
   2.669 -        int leftTriggerMapping = gamecontroller->mapping.axes[SDL_CONTROLLER_AXIS_TRIGGERLEFT];
   2.670 -        int rightTriggerMapping = gamecontroller->mapping.axes[SDL_CONTROLLER_AXIS_TRIGGERRIGHT];
   2.671 -        if (leftTriggerMapping >= 0) {
   2.672 -            gamecontroller->joystick->axes[leftTriggerMapping].value =
   2.673 -            gamecontroller->joystick->axes[leftTriggerMapping].zero = (Sint16)-32768;
   2.674 -        }
   2.675 -        if (rightTriggerMapping >= 0) {
   2.676 -            gamecontroller->joystick->axes[rightTriggerMapping].value =
   2.677 -            gamecontroller->joystick->axes[rightTriggerMapping].zero = (Sint16)-32768;
   2.678 -        }
   2.679 -    }
   2.680 +    SDL_PrivateLoadButtonMapping(gamecontroller, pSupportedController->guid, pSupportedController->name, pSupportedController->mapping);
   2.681  
   2.682      /* Add joystick to list */
   2.683      ++gamecontroller->ref_count;
   2.684 @@ -1162,65 +1231,102 @@
   2.685      SDL_JoystickUpdate();
   2.686  }
   2.687  
   2.688 -
   2.689  /*
   2.690   * Get the current state of an axis control on a controller
   2.691   */
   2.692  Sint16
   2.693  SDL_GameControllerGetAxis(SDL_GameController * gamecontroller, SDL_GameControllerAxis axis)
   2.694  {
   2.695 +    int i;
   2.696 +
   2.697      if (!gamecontroller)
   2.698          return 0;
   2.699  
   2.700 -    if (gamecontroller->mapping.axes[axis] >= 0) {
   2.701 -        Sint16 value = (SDL_JoystickGetAxis(gamecontroller->joystick, gamecontroller->mapping.axes[axis]));
   2.702 -        switch (axis) {
   2.703 -            case SDL_CONTROLLER_AXIS_TRIGGERLEFT:
   2.704 -            case SDL_CONTROLLER_AXIS_TRIGGERRIGHT:
   2.705 -                /* Shift it to be 0 - 32767 */
   2.706 -                value = value / 2 + 16384;
   2.707 -            default:
   2.708 -                break;
   2.709 +    for (i = 0; i < gamecontroller->num_bindings; ++i) {
   2.710 +        SDL_ExtendedGameControllerBind *binding = &gamecontroller->bindings[i];
   2.711 +        if (binding->outputType == SDL_CONTROLLER_BINDTYPE_AXIS && binding->output.axis.axis == axis) {
   2.712 +            int value = 0;
   2.713 +            SDL_bool valid_input_range;
   2.714 +            SDL_bool valid_output_range;
   2.715 +
   2.716 +            if (binding->inputType == SDL_CONTROLLER_BINDTYPE_AXIS) {
   2.717 +                value = SDL_JoystickGetAxis(gamecontroller->joystick, binding->input.axis.axis);
   2.718 +                if (binding->input.axis.axis_min < binding->input.axis.axis_max) {
   2.719 +                    valid_input_range = (value >= binding->input.axis.axis_min && value <= binding->input.axis.axis_max);
   2.720 +                } else {
   2.721 +                    valid_input_range = (value >= binding->input.axis.axis_max && value <= binding->input.axis.axis_min);
   2.722 +                }
   2.723 +                if (valid_input_range) {
   2.724 +                    if (binding->input.axis.axis_min != binding->output.axis.axis_min || binding->input.axis.axis_max != binding->output.axis.axis_max) {
   2.725 +                        float normalized_value = (float)(value - binding->input.axis.axis_min) / (binding->input.axis.axis_max - binding->input.axis.axis_min);
   2.726 +                        value = binding->output.axis.axis_min + (int)(normalized_value * (binding->output.axis.axis_max - binding->output.axis.axis_min));
   2.727 +                    }
   2.728 +                }
   2.729 +            } else if (binding->inputType == SDL_CONTROLLER_BINDTYPE_BUTTON) {
   2.730 +                value = SDL_JoystickGetButton(gamecontroller->joystick, binding->input.button);
   2.731 +                if (value == SDL_PRESSED) {
   2.732 +                    value = binding->output.axis.axis_max;
   2.733 +                }
   2.734 +            } else if (binding->inputType == SDL_CONTROLLER_BINDTYPE_HAT) {
   2.735 +                int hat_mask = SDL_JoystickGetHat(gamecontroller->joystick, binding->input.hat.hat);
   2.736 +                if (hat_mask & binding->input.hat.hat_mask) {
   2.737 +                    value = binding->output.axis.axis_max;
   2.738 +                }
   2.739 +            }
   2.740 +
   2.741 +            if (binding->output.axis.axis_min < binding->output.axis.axis_max) {
   2.742 +                valid_output_range = (value >= binding->output.axis.axis_min && value <= binding->output.axis.axis_max);
   2.743 +            } else {
   2.744 +                valid_output_range = (value >= binding->output.axis.axis_max && value <= binding->output.axis.axis_min);
   2.745 +            }
   2.746 +            // If the value is zero, there might be another binding that makes it non-zero
   2.747 +            if (value != 0 && valid_output_range) {
   2.748 +                return (Sint16)value;
   2.749 +            }
   2.750          }
   2.751 -        return value;
   2.752 -    } else if (gamecontroller->mapping.buttonasaxis[axis] >= 0) {
   2.753 -        Uint8 value;
   2.754 -        value = SDL_JoystickGetButton(gamecontroller->joystick, gamecontroller->mapping.buttonasaxis[axis]);
   2.755 -        if (value > 0)
   2.756 -            return 32767;
   2.757 -        return 0;
   2.758      }
   2.759      return 0;
   2.760  }
   2.761  
   2.762 -
   2.763  /*
   2.764   * Get the current state of a button on a controller
   2.765   */
   2.766  Uint8
   2.767  SDL_GameControllerGetButton(SDL_GameController * gamecontroller, SDL_GameControllerButton button)
   2.768  {
   2.769 +    int i;
   2.770 +
   2.771      if (!gamecontroller)
   2.772          return 0;
   2.773  
   2.774 -    if (gamecontroller->mapping.buttons[button] >= 0) {
   2.775 -        return (SDL_JoystickGetButton(gamecontroller->joystick, gamecontroller->mapping.buttons[button]));
   2.776 -    } else if (gamecontroller->mapping.axesasbutton[button] >= 0) {
   2.777 -        Sint16 value;
   2.778 -        value = SDL_JoystickGetAxis(gamecontroller->joystick, gamecontroller->mapping.axesasbutton[button]);
   2.779 -        if (ABS(value) > 32768/2)
   2.780 -            return 1;
   2.781 -        return 0;
   2.782 -    } else if (gamecontroller->mapping.hatasbutton[button].hat >= 0) {
   2.783 -        Uint8 value;
   2.784 -        value = SDL_JoystickGetHat(gamecontroller->joystick, gamecontroller->mapping.hatasbutton[button].hat);
   2.785 +    for (i = 0; i < gamecontroller->num_bindings; ++i) {
   2.786 +        SDL_ExtendedGameControllerBind *binding = &gamecontroller->bindings[i];
   2.787 +        if (binding->outputType == SDL_CONTROLLER_BINDTYPE_BUTTON && binding->output.button == button) {
   2.788 +            if (binding->inputType == SDL_CONTROLLER_BINDTYPE_AXIS) {
   2.789 +                SDL_bool valid_input_range;
   2.790  
   2.791 -        if (value & gamecontroller->mapping.hatasbutton[button].mask)
   2.792 -            return 1;
   2.793 -        return 0;
   2.794 +                int value = SDL_JoystickGetAxis(gamecontroller->joystick, binding->input.axis.axis);
   2.795 +                int threshold = binding->input.axis.axis_min + (binding->input.axis.axis_max - binding->input.axis.axis_min) / 2;
   2.796 +                if (binding->input.axis.axis_min < binding->input.axis.axis_max) {
   2.797 +                    valid_input_range = (value >= binding->input.axis.axis_min && value <= binding->input.axis.axis_max);
   2.798 +                    if (valid_input_range) {
   2.799 +                        return (value >= threshold) ? SDL_PRESSED : SDL_RELEASED;
   2.800 +                    }
   2.801 +                } else {
   2.802 +                    valid_input_range = (value >= binding->input.axis.axis_max && value <= binding->input.axis.axis_min);
   2.803 +                    if (valid_input_range) {
   2.804 +                        return (value <= threshold) ? SDL_PRESSED : SDL_RELEASED;
   2.805 +                    }
   2.806 +                }
   2.807 +            } else if (binding->inputType == SDL_CONTROLLER_BINDTYPE_BUTTON) {
   2.808 +                return SDL_JoystickGetButton(gamecontroller->joystick, binding->input.button);
   2.809 +            } else if (binding->inputType == SDL_CONTROLLER_BINDTYPE_HAT) {
   2.810 +                int hat_mask = SDL_JoystickGetHat(gamecontroller->joystick, binding->input.hat.hat);
   2.811 +                return (hat_mask & binding->input.hat.hat_mask) ? SDL_PRESSED : SDL_RELEASED;
   2.812 +            }
   2.813 +        }
   2.814      }
   2.815 -
   2.816 -    return 0;
   2.817 +    return SDL_RELEASED;
   2.818  }
   2.819  
   2.820  const char *
   2.821 @@ -1229,7 +1335,7 @@
   2.822      if (!gamecontroller)
   2.823          return NULL;
   2.824  
   2.825 -    return gamecontroller->mapping.name;
   2.826 +    return gamecontroller->name;
   2.827  }
   2.828  
   2.829  Uint16
   2.830 @@ -1302,20 +1408,29 @@
   2.831   */
   2.832  SDL_GameControllerButtonBind SDL_GameControllerGetBindForAxis(SDL_GameController * gamecontroller, SDL_GameControllerAxis axis)
   2.833  {
   2.834 +    int i;
   2.835      SDL_GameControllerButtonBind bind;
   2.836 -    SDL_memset(&bind, 0x0, sizeof(bind));
   2.837 +    SDL_zero(bind);
   2.838  
   2.839      if (!gamecontroller || axis == SDL_CONTROLLER_AXIS_INVALID)
   2.840          return bind;
   2.841  
   2.842 -    if (gamecontroller->mapping.axes[axis] >= 0) {
   2.843 -        bind.bindType = SDL_CONTROLLER_BINDTYPE_AXIS;
   2.844 -        bind.value.button = gamecontroller->mapping.axes[axis];
   2.845 -    } else if (gamecontroller->mapping.buttonasaxis[axis] >= 0) {
   2.846 -        bind.bindType = SDL_CONTROLLER_BINDTYPE_BUTTON;
   2.847 -        bind.value.button = gamecontroller->mapping.buttonasaxis[axis];
   2.848 +    for (i = 0; i < gamecontroller->num_bindings; ++i) {
   2.849 +        SDL_ExtendedGameControllerBind *binding = &gamecontroller->bindings[i];
   2.850 +        if (binding->outputType == SDL_CONTROLLER_BINDTYPE_AXIS && binding->output.axis.axis == axis) {
   2.851 +            bind.bindType = binding->inputType;
   2.852 +            if (binding->inputType == SDL_CONTROLLER_BINDTYPE_AXIS) {
   2.853 +                /* FIXME: There might be multiple axes bound now that we have axis ranges... */
   2.854 +                bind.value.axis = binding->input.axis.axis;
   2.855 +            } else if (binding->inputType == SDL_CONTROLLER_BINDTYPE_BUTTON) {
   2.856 +                bind.value.button = binding->input.button;
   2.857 +            } else if (binding->inputType == SDL_CONTROLLER_BINDTYPE_HAT) {
   2.858 +                bind.value.hat.hat = binding->input.hat.hat;
   2.859 +                bind.value.hat.hat_mask = binding->input.hat.hat_mask;
   2.860 +            }
   2.861 +            break;
   2.862 +        }
   2.863      }
   2.864 -
   2.865      return bind;
   2.866  }
   2.867  
   2.868 @@ -1325,24 +1440,28 @@
   2.869   */
   2.870  SDL_GameControllerButtonBind SDL_GameControllerGetBindForButton(SDL_GameController * gamecontroller, SDL_GameControllerButton button)
   2.871  {
   2.872 +    int i;
   2.873      SDL_GameControllerButtonBind bind;
   2.874 -    SDL_memset(&bind, 0x0, sizeof(bind));
   2.875 +    SDL_zero(bind);
   2.876  
   2.877      if (!gamecontroller || button == SDL_CONTROLLER_BUTTON_INVALID)
   2.878          return bind;
   2.879  
   2.880 -    if (gamecontroller->mapping.buttons[button] >= 0) {
   2.881 -        bind.bindType = SDL_CONTROLLER_BINDTYPE_BUTTON;
   2.882 -        bind.value.button = gamecontroller->mapping.buttons[button];
   2.883 -    } else if (gamecontroller->mapping.axesasbutton[button] >= 0) {
   2.884 -        bind.bindType = SDL_CONTROLLER_BINDTYPE_AXIS;
   2.885 -        bind.value.axis = gamecontroller->mapping.axesasbutton[button];
   2.886 -    } else if (gamecontroller->mapping.hatasbutton[button].hat >= 0) {
   2.887 -        bind.bindType = SDL_CONTROLLER_BINDTYPE_HAT;
   2.888 -        bind.value.hat.hat = gamecontroller->mapping.hatasbutton[button].hat;
   2.889 -        bind.value.hat.hat_mask = gamecontroller->mapping.hatasbutton[button].mask;
   2.890 +    for (i = 0; i < gamecontroller->num_bindings; ++i) {
   2.891 +        SDL_ExtendedGameControllerBind *binding = &gamecontroller->bindings[i];
   2.892 +        if (binding->outputType == SDL_CONTROLLER_BINDTYPE_BUTTON && binding->output.button == button) {
   2.893 +            bind.bindType = binding->inputType;
   2.894 +            if (binding->inputType == SDL_CONTROLLER_BINDTYPE_AXIS) {
   2.895 +                bind.value.axis = binding->input.axis.axis;
   2.896 +            } else if (binding->inputType == SDL_CONTROLLER_BINDTYPE_BUTTON) {
   2.897 +                bind.value.button = binding->input.button;
   2.898 +            } else if (binding->inputType == SDL_CONTROLLER_BINDTYPE_HAT) {
   2.899 +                bind.value.hat.hat = binding->input.hat.hat;
   2.900 +                bind.value.hat.hat_mask = binding->input.hat.hat_mask;
   2.901 +            }
   2.902 +            break;
   2.903 +        }
   2.904      }
   2.905 -
   2.906      return bind;
   2.907  }
   2.908  
   2.909 @@ -1381,6 +1500,9 @@
   2.910          gamecontrollerlist = gamecontrollerlist->next;
   2.911      }
   2.912  
   2.913 +    SDL_free(gamecontroller->bindings);
   2.914 +    SDL_free(gamecontroller->last_match_axis);
   2.915 +    SDL_free(gamecontroller->last_hat_mask);
   2.916      SDL_free(gamecontroller);
   2.917  
   2.918      SDL_UnlockJoystickList();
   2.919 @@ -1417,7 +1539,7 @@
   2.920  /*
   2.921   * Event filter to transform joystick events into appropriate game controller ones
   2.922   */
   2.923 -int
   2.924 +static int
   2.925  SDL_PrivateGameControllerAxis(SDL_GameController * gamecontroller, SDL_GameControllerAxis axis, Sint16 value)
   2.926  {
   2.927      int posted;
   2.928 @@ -1441,7 +1563,7 @@
   2.929  /*
   2.930   * Event filter to transform joystick events into appropriate game controller ones
   2.931   */
   2.932 -int
   2.933 +static int
   2.934  SDL_PrivateGameControllerButton(SDL_GameController * gamecontroller, SDL_GameControllerButton button, Uint8 state)
   2.935  {
   2.936      int posted;
     3.1 --- a/src/joystick/windows/SDL_dinputjoystick.c	Mon Dec 26 23:02:14 2016 -0500
     3.2 +++ b/src/joystick/windows/SDL_dinputjoystick.c	Tue Dec 27 01:39:07 2016 -0800
     3.3 @@ -33,9 +33,7 @@
     3.4  #endif
     3.5  
     3.6  #define INPUT_QSIZE 32      /* Buffer up to 32 input messages */
     3.7 -#define AXIS_MIN    -32768  /* minimum value for axis coordinate */
     3.8 -#define AXIS_MAX    32767   /* maximum value for axis coordinate */
     3.9 -#define JOY_AXIS_THRESHOLD  (((AXIS_MAX)-(AXIS_MIN))/100)   /* 1% motion */
    3.10 +#define JOY_AXIS_THRESHOLD  (((SDL_JOYSTICK_AXIS_MAX)-(SDL_JOYSTICK_AXIS_MIN))/100)   /* 1% motion */
    3.11  
    3.12  /* external variables referenced. */
    3.13  extern HWND SDL_HelperWindow;
    3.14 @@ -481,8 +479,8 @@
    3.15          diprg.diph.dwHeaderSize = sizeof(diprg.diph);
    3.16          diprg.diph.dwObj = dev->dwType;
    3.17          diprg.diph.dwHow = DIPH_BYID;
    3.18 -        diprg.lMin = AXIS_MIN;
    3.19 -        diprg.lMax = AXIS_MAX;
    3.20 +        diprg.lMin = SDL_JOYSTICK_AXIS_MIN;
    3.21 +        diprg.lMax = SDL_JOYSTICK_AXIS_MAX;
    3.22  
    3.23          result =
    3.24              IDirectInputDevice8_SetProperty(joystick->hwdata->InputDevice,
     4.1 --- a/src/joystick/windows/SDL_mmjoystick.c	Mon Dec 26 23:02:14 2016 -0500
     4.2 +++ b/src/joystick/windows/SDL_mmjoystick.c	Tue Dec 27 01:39:07 2016 -0800
     4.3 @@ -41,10 +41,8 @@
     4.4  #define MAX_JOYSTICKS   16
     4.5  #define MAX_AXES    6       /* each joystick can have up to 6 axes */
     4.6  #define MAX_BUTTONS 32      /* and 32 buttons                      */
     4.7 -#define AXIS_MIN    -32768  /* minimum value for axis coordinate */
     4.8 -#define AXIS_MAX    32767   /* maximum value for axis coordinate */
     4.9  /* limit axis to 256 possible positions to filter out noise */
    4.10 -#define JOY_AXIS_THRESHOLD      (((AXIS_MAX)-(AXIS_MIN))/256)
    4.11 +#define JOY_AXIS_THRESHOLD      (((SDL_JOYSTICK_AXIS_MAX)-(SDL_JOYSTICK_AXIS_MIN))/256)
    4.12  #define JOY_BUTTON_FLAG(n)  (1<<n)
    4.13  
    4.14  
    4.15 @@ -253,9 +251,9 @@
    4.16      joystick->hwdata->id = SYS_JoystickID[index];
    4.17      for (i = 0; i < MAX_AXES; ++i) {
    4.18          if ((i < 2) || (SYS_Joystick[index].wCaps & caps_flags[i - 2])) {
    4.19 -            joystick->hwdata->transaxis[i].offset = AXIS_MIN - axis_min[i];
    4.20 +            joystick->hwdata->transaxis[i].offset = SDL_JOYSTICK_AXIS_MIN - axis_min[i];
    4.21              joystick->hwdata->transaxis[i].scale =
    4.22 -                (float) (AXIS_MAX - AXIS_MIN) / (axis_max[i] - axis_min[i]);
    4.23 +                (float) (SDL_JOYSTICK_AXIS_MAX - SDL_JOYSTICK_AXIS_MIN) / (axis_max[i] - axis_min[i]);
    4.24          } else {
    4.25              joystick->hwdata->transaxis[i].offset = 0;
    4.26              joystick->hwdata->transaxis[i].scale = 1.0; /* Just in case */
     5.1 Binary file test/axis.bmp has changed
     6.1 --- a/test/controllermap.c	Mon Dec 26 23:02:14 2016 -0500
     6.2 +++ b/test/controllermap.c	Tue Dec 27 01:39:07 2016 -0800
     6.3 @@ -32,7 +32,22 @@
     6.4  #define MARKER_BUTTON 1
     6.5  #define MARKER_AXIS 2
     6.6  
     6.7 -#define BINDING_COUNT (SDL_CONTROLLER_BUTTON_MAX + SDL_CONTROLLER_AXIS_MAX)
     6.8 +enum
     6.9 +{
    6.10 +    SDL_CONTROLLER_BINDING_AXIS_LEFTX_NEGATIVE,
    6.11 +    SDL_CONTROLLER_BINDING_AXIS_LEFTX_POSITIVE,
    6.12 +    SDL_CONTROLLER_BINDING_AXIS_LEFTY_NEGATIVE,
    6.13 +    SDL_CONTROLLER_BINDING_AXIS_LEFTY_POSITIVE,
    6.14 +    SDL_CONTROLLER_BINDING_AXIS_RIGHTX_NEGATIVE,
    6.15 +    SDL_CONTROLLER_BINDING_AXIS_RIGHTX_POSITIVE,
    6.16 +    SDL_CONTROLLER_BINDING_AXIS_RIGHTY_NEGATIVE,
    6.17 +    SDL_CONTROLLER_BINDING_AXIS_RIGHTY_POSITIVE,
    6.18 +    SDL_CONTROLLER_BINDING_AXIS_TRIGGERLEFT,
    6.19 +    SDL_CONTROLLER_BINDING_AXIS_TRIGGERRIGHT,
    6.20 +    SDL_CONTROLLER_BINDING_AXIS_MAX,
    6.21 +};
    6.22 +
    6.23 +#define BINDING_COUNT (SDL_CONTROLLER_BUTTON_MAX + SDL_CONTROLLER_BINDING_AXIS_MAX)
    6.24  
    6.25  static struct 
    6.26  {
    6.27 @@ -56,12 +71,16 @@
    6.28      { 154, 249, 0.0, MARKER_BUTTON }, /* SDL_CONTROLLER_BUTTON_DPAD_DOWN */
    6.29      { 116, 217, 0.0, MARKER_BUTTON }, /* SDL_CONTROLLER_BUTTON_DPAD_LEFT */
    6.30      { 186, 217, 0.0, MARKER_BUTTON }, /* SDL_CONTROLLER_BUTTON_DPAD_RIGHT */
    6.31 -    {  75, 154, 0.0,   MARKER_AXIS }, /* SDL_CONTROLLER_AXIS_LEFTX */
    6.32 -    {  75, 154, 90.0,  MARKER_AXIS }, /* SDL_CONTROLLER_AXIS_LEFTY */
    6.33 -    { 305, 230, 0.0,   MARKER_AXIS }, /* SDL_CONTROLLER_AXIS_RIGHTX */
    6.34 -    { 305, 230, 90.0,  MARKER_AXIS }, /* SDL_CONTROLLER_AXIS_RIGHTY */
    6.35 -    {  91,   0, 90.0,  MARKER_AXIS }, /* SDL_CONTROLLER_AXIS_TRIGGERLEFT */
    6.36 -    { 375,   0, 90.0,  MARKER_AXIS }, /* SDL_CONTROLLER_AXIS_TRIGGERRIGHT */
    6.37 +    {  74, 153, 270.0, MARKER_AXIS }, /* SDL_CONTROLLER_BINDING_AXIS_LEFTX_NEGATIVE */
    6.38 +    {  74, 153, 90.0,  MARKER_AXIS }, /* SDL_CONTROLLER_BINDING_AXIS_LEFTX_POSITIVE */
    6.39 +    {  74, 153, 0.0,   MARKER_AXIS }, /* SDL_CONTROLLER_BINDING_AXIS_LEFTY_NEGATIVE */
    6.40 +    {  74, 153, 180.0, MARKER_AXIS }, /* SDL_CONTROLLER_BINDING_AXIS_LEFTY_POSITIVE */
    6.41 +    { 306, 231, 270.0, MARKER_AXIS }, /* SDL_CONTROLLER_BINDING_AXIS_RIGHTX_NEGATIVE */
    6.42 +    { 306, 231, 90.0,  MARKER_AXIS }, /* SDL_CONTROLLER_BINDING_AXIS_RIGHTX_POSITIVE */
    6.43 +    { 306, 231, 0.0,   MARKER_AXIS }, /* SDL_CONTROLLER_BINDING_AXIS_RIGHTY_NEGATIVE */
    6.44 +    { 306, 231, 180.0, MARKER_AXIS }, /* SDL_CONTROLLER_BINDING_AXIS_RIGHTY_POSITIVE */
    6.45 +    {  91, -20, 180.0, MARKER_AXIS }, /* SDL_CONTROLLER_BINDING_AXIS_TRIGGERLEFT */
    6.46 +    { 375, -20, 180.0, MARKER_AXIS }, /* SDL_CONTROLLER_BINDING_AXIS_TRIGGERRIGHT */
    6.47  };
    6.48  
    6.49  static int s_arrBindingOrder[BINDING_COUNT] = {
    6.50 @@ -69,16 +88,20 @@
    6.51      SDL_CONTROLLER_BUTTON_B,
    6.52      SDL_CONTROLLER_BUTTON_Y,
    6.53      SDL_CONTROLLER_BUTTON_X,
    6.54 -    SDL_CONTROLLER_BUTTON_MAX + SDL_CONTROLLER_AXIS_LEFTX,
    6.55 -    SDL_CONTROLLER_BUTTON_MAX + SDL_CONTROLLER_AXIS_LEFTY,
    6.56 +    SDL_CONTROLLER_BUTTON_MAX + SDL_CONTROLLER_BINDING_AXIS_LEFTX_NEGATIVE,
    6.57 +    SDL_CONTROLLER_BUTTON_MAX + SDL_CONTROLLER_BINDING_AXIS_LEFTX_POSITIVE,
    6.58 +    SDL_CONTROLLER_BUTTON_MAX + SDL_CONTROLLER_BINDING_AXIS_LEFTY_NEGATIVE,
    6.59 +    SDL_CONTROLLER_BUTTON_MAX + SDL_CONTROLLER_BINDING_AXIS_LEFTY_POSITIVE,
    6.60      SDL_CONTROLLER_BUTTON_LEFTSTICK,
    6.61 -    SDL_CONTROLLER_BUTTON_MAX + SDL_CONTROLLER_AXIS_RIGHTX,
    6.62 -    SDL_CONTROLLER_BUTTON_MAX + SDL_CONTROLLER_AXIS_RIGHTY,
    6.63 +    SDL_CONTROLLER_BUTTON_MAX + SDL_CONTROLLER_BINDING_AXIS_RIGHTX_NEGATIVE,
    6.64 +    SDL_CONTROLLER_BUTTON_MAX + SDL_CONTROLLER_BINDING_AXIS_RIGHTX_POSITIVE,
    6.65 +    SDL_CONTROLLER_BUTTON_MAX + SDL_CONTROLLER_BINDING_AXIS_RIGHTY_NEGATIVE,
    6.66 +    SDL_CONTROLLER_BUTTON_MAX + SDL_CONTROLLER_BINDING_AXIS_RIGHTY_POSITIVE,
    6.67      SDL_CONTROLLER_BUTTON_RIGHTSTICK,
    6.68      SDL_CONTROLLER_BUTTON_LEFTSHOULDER,
    6.69 -    SDL_CONTROLLER_BUTTON_MAX + SDL_CONTROLLER_AXIS_TRIGGERLEFT,
    6.70 +    SDL_CONTROLLER_BUTTON_MAX + SDL_CONTROLLER_BINDING_AXIS_TRIGGERLEFT,
    6.71      SDL_CONTROLLER_BUTTON_RIGHTSHOULDER,
    6.72 -    SDL_CONTROLLER_BUTTON_MAX + SDL_CONTROLLER_AXIS_TRIGGERRIGHT,
    6.73 +    SDL_CONTROLLER_BUTTON_MAX + SDL_CONTROLLER_BINDING_AXIS_TRIGGERRIGHT,
    6.74      SDL_CONTROLLER_BUTTON_DPAD_UP,
    6.75      SDL_CONTROLLER_BUTTON_DPAD_RIGHT,
    6.76      SDL_CONTROLLER_BUTTON_DPAD_DOWN,
    6.77 @@ -88,7 +111,40 @@
    6.78      SDL_CONTROLLER_BUTTON_START,
    6.79  };
    6.80  
    6.81 -static SDL_GameControllerButtonBind s_arrBindings[BINDING_COUNT];
    6.82 +typedef struct
    6.83 +{
    6.84 +    SDL_GameControllerBindType bindType;
    6.85 +    union
    6.86 +    {
    6.87 +        int button;
    6.88 +
    6.89 +        struct {
    6.90 +            int axis;
    6.91 +            int axis_min;
    6.92 +            int axis_max;
    6.93 +        } axis;
    6.94 +
    6.95 +        struct {
    6.96 +            int hat;
    6.97 +            int hat_mask;
    6.98 +        } hat;
    6.99 +
   6.100 +    } value;
   6.101 +
   6.102 +} SDL_GameControllerExtendedBind;
   6.103 +
   6.104 +static SDL_GameControllerExtendedBind s_arrBindings[BINDING_COUNT];
   6.105 +
   6.106 +typedef struct
   6.107 +{
   6.108 +    SDL_bool m_bMoving;
   6.109 +    int m_nStartingValue;
   6.110 +    int m_nFarthestValue;
   6.111 +} AxisState;
   6.112 +
   6.113 +static int s_nNumAxes;
   6.114 +static AxisState *s_arrAxisState;
   6.115 +    
   6.116  static int s_iCurrentBinding;
   6.117  static Uint32 s_unPendingAdvanceTime;
   6.118  static SDL_bool s_bBindingComplete;
   6.119 @@ -110,23 +166,6 @@
   6.120      if (transparent) {
   6.121          if (temp->format->palette) {
   6.122              SDL_SetColorKey(temp, SDL_TRUE, *(Uint8 *) temp->pixels);
   6.123 -        } else {
   6.124 -            switch (temp->format->BitsPerPixel) {
   6.125 -            case 15:
   6.126 -                SDL_SetColorKey(temp, SDL_TRUE,
   6.127 -                                (*(Uint16 *) temp->pixels) & 0x00007FFF);
   6.128 -                break;
   6.129 -            case 16:
   6.130 -                SDL_SetColorKey(temp, SDL_TRUE, *(Uint16 *) temp->pixels);
   6.131 -                break;
   6.132 -            case 24:
   6.133 -                SDL_SetColorKey(temp, SDL_TRUE,
   6.134 -                                (*(Uint32 *) temp->pixels) & 0x00FFFFFF);
   6.135 -                break;
   6.136 -            case 32:
   6.137 -                SDL_SetColorKey(temp, SDL_TRUE, *(Uint32 *) temp->pixels);
   6.138 -                break;
   6.139 -            }
   6.140          }
   6.141      }
   6.142  
   6.143 @@ -143,9 +182,22 @@
   6.144      return texture;
   6.145  }
   6.146  
   6.147 -void SetCurrentBinding(int iBinding)
   6.148 +static int
   6.149 +StandardizeAxisValue(int nValue)
   6.150  {
   6.151 -    SDL_GameControllerButtonBind *pBinding;
   6.152 +    if (nValue > SDL_JOYSTICK_AXIS_MAX/2) {
   6.153 +        return SDL_JOYSTICK_AXIS_MAX;
   6.154 +    } else if (nValue < SDL_JOYSTICK_AXIS_MIN/2) {
   6.155 +        return SDL_JOYSTICK_AXIS_MIN;
   6.156 +    } else {
   6.157 +        return 0;
   6.158 +    }
   6.159 +}
   6.160 +
   6.161 +static void
   6.162 +SetCurrentBinding(int iBinding)
   6.163 +{
   6.164 +    SDL_GameControllerExtendedBind *pBinding;
   6.165  
   6.166      if (iBinding < 0) {
   6.167          return;
   6.168 @@ -161,14 +213,15 @@
   6.169      pBinding = &s_arrBindings[s_arrBindingOrder[s_iCurrentBinding]];
   6.170      SDL_zerop(pBinding);
   6.171  
   6.172 +    SDL_memset(s_arrAxisState, 0, s_nNumAxes*sizeof(*s_arrAxisState));
   6.173 +
   6.174      s_unPendingAdvanceTime = 0;
   6.175  }
   6.176  
   6.177 -
   6.178  static void
   6.179 -ConfigureBinding(const SDL_GameControllerButtonBind *pBinding)
   6.180 +ConfigureBinding(const SDL_GameControllerExtendedBind *pBinding)
   6.181  {
   6.182 -    SDL_GameControllerButtonBind *pCurrent;
   6.183 +    SDL_GameControllerExtendedBind *pCurrent;
   6.184      int iIndex;
   6.185      int iCurrentElement = s_arrBindingOrder[s_iCurrentBinding];
   6.186  
   6.187 @@ -221,6 +274,24 @@
   6.188      s_unPendingAdvanceTime = SDL_GetTicks();
   6.189  }
   6.190  
   6.191 +static SDL_bool
   6.192 +BMergeAxisBindings(int iIndex)
   6.193 +{
   6.194 +    SDL_GameControllerExtendedBind *pBindingA = &s_arrBindings[iIndex];
   6.195 +    SDL_GameControllerExtendedBind *pBindingB = &s_arrBindings[iIndex+1];
   6.196 +    if (pBindingA->bindType == SDL_CONTROLLER_BINDTYPE_AXIS &&
   6.197 +        pBindingB->bindType == SDL_CONTROLLER_BINDTYPE_AXIS &&
   6.198 +        pBindingA->value.axis.axis == pBindingB->value.axis.axis) {
   6.199 +        if (pBindingA->value.axis.axis_min == pBindingB->value.axis.axis_min) {
   6.200 +            pBindingA->value.axis.axis_min = pBindingA->value.axis.axis_max;
   6.201 +            pBindingA->value.axis.axis_max = pBindingB->value.axis.axis_max;
   6.202 +            pBindingB->bindType = SDL_CONTROLLER_BINDTYPE_NONE;
   6.203 +            return SDL_TRUE;
   6.204 +        }
   6.205 +    }
   6.206 +    return SDL_FALSE;
   6.207 +}
   6.208 +
   6.209  static void
   6.210  WatchJoystick(SDL_Joystick * joystick)
   6.211  {
   6.212 @@ -279,6 +350,9 @@
   6.213  
   6.214      nJoystickID = SDL_JoystickInstanceID(joystick);
   6.215  
   6.216 +    s_nNumAxes = SDL_JoystickNumAxes(joystick);
   6.217 +    s_arrAxisState = SDL_calloc(s_nNumAxes, sizeof(*s_arrAxisState));
   6.218 +
   6.219      /* Loop, getting joystick events! */
   6.220      while (!done && !s_bBindingComplete) {
   6.221          int iElement = s_arrBindingOrder[s_iCurrentBinding];
   6.222 @@ -326,26 +400,35 @@
   6.223                  break;
   6.224              case SDL_JOYAXISMOTION:
   6.225                  if (event.jaxis.which == nJoystickID) {
   6.226 -                    uint32_t unAxisMask = (1 << event.jaxis.axis);
   6.227 -                    SDL_bool bDeflected = (event.jaxis.value <= -20000 || event.jaxis.value >= 20000);
   6.228 -                    if (bDeflected && !(unDeflectedAxes & unAxisMask)) {
   6.229 -                        SDL_GameControllerButtonBind binding;
   6.230 +                    AxisState *pAxisState = &s_arrAxisState[event.jaxis.axis];
   6.231 +                    int nValue = event.jaxis.value;
   6.232 +                    int nCurrentDistance, nFarthestDistance;
   6.233 +                    if (!pAxisState->m_bMoving) {
   6.234 +                        pAxisState->m_bMoving = SDL_TRUE;
   6.235 +                        pAxisState->m_nStartingValue = nValue;
   6.236 +                        pAxisState->m_nFarthestValue = nValue;
   6.237 +                    }
   6.238 +                    nCurrentDistance = SDL_abs(nValue - pAxisState->m_nStartingValue);
   6.239 +                    nFarthestDistance = SDL_abs(pAxisState->m_nFarthestValue - pAxisState->m_nStartingValue);
   6.240 +                    if (nCurrentDistance > nFarthestDistance) {
   6.241 +                        pAxisState->m_nFarthestValue = nValue;
   6.242 +                    }
   6.243 +                    if (nCurrentDistance < 10000 && nFarthestDistance > 20000) {
   6.244 +                        /* We've gone out and back, let's bind this axis */
   6.245 +                        SDL_GameControllerExtendedBind binding;
   6.246                          SDL_zero(binding);
   6.247                          binding.bindType = SDL_CONTROLLER_BINDTYPE_AXIS;
   6.248 -                        binding.value.axis = event.jaxis.axis;
   6.249 +                        binding.value.axis.axis = event.jaxis.axis;
   6.250 +                        binding.value.axis.axis_min = StandardizeAxisValue(pAxisState->m_nStartingValue);
   6.251 +                        binding.value.axis.axis_max = StandardizeAxisValue(pAxisState->m_nFarthestValue);
   6.252                          ConfigureBinding(&binding);
   6.253                      }
   6.254 -                    if (bDeflected) {
   6.255 -                        unDeflectedAxes |= unAxisMask;
   6.256 -                    } else {
   6.257 -                        unDeflectedAxes &= ~unAxisMask;
   6.258 -                    }
   6.259                  }
   6.260                  break;
   6.261              case SDL_JOYHATMOTION:
   6.262                  if (event.jhat.which == nJoystickID) {
   6.263                      if (event.jhat.value != SDL_HAT_CENTERED) {
   6.264 -                        SDL_GameControllerButtonBind binding;
   6.265 +                        SDL_GameControllerExtendedBind binding;
   6.266                          SDL_zero(binding);
   6.267                          binding.bindType = SDL_CONTROLLER_BINDTYPE_HAT;
   6.268                          binding.value.hat.hat = event.jhat.hat;
   6.269 @@ -358,7 +441,7 @@
   6.270                  break;
   6.271              case SDL_JOYBUTTONDOWN:
   6.272                  if (event.jbutton.which == nJoystickID) {
   6.273 -                    SDL_GameControllerButtonBind binding;
   6.274 +                    SDL_GameControllerExtendedBind binding;
   6.275                      SDL_zero(binding);
   6.276                      binding.bindType = SDL_CONTROLLER_BINDTYPE_BUTTON;
   6.277                      binding.value.button = event.jbutton.button;
   6.278 @@ -430,7 +513,7 @@
   6.279          SDL_strlcat(mapping, ",", SDL_arraysize(mapping));
   6.280  
   6.281          for (iIndex = 0; iIndex < SDL_arraysize(s_arrBindings); ++iIndex) {
   6.282 -            SDL_GameControllerButtonBind *pBinding = &s_arrBindings[iIndex];
   6.283 +            SDL_GameControllerExtendedBind *pBinding = &s_arrBindings[iIndex];
   6.284              if (pBinding->bindType == SDL_CONTROLLER_BINDTYPE_NONE) {
   6.285                  continue;
   6.286              }
   6.287 @@ -439,8 +522,56 @@
   6.288                  SDL_GameControllerButton eButton = (SDL_GameControllerButton)iIndex;
   6.289                  SDL_strlcat(mapping, SDL_GameControllerGetStringForButton(eButton), SDL_arraysize(mapping));
   6.290              } else {
   6.291 -                SDL_GameControllerAxis eAxis = (SDL_GameControllerAxis)(iIndex - SDL_CONTROLLER_BUTTON_MAX);
   6.292 -                SDL_strlcat(mapping, SDL_GameControllerGetStringForAxis(eAxis), SDL_arraysize(mapping));
   6.293 +                const char *pszAxisName;
   6.294 +                switch (iIndex - SDL_CONTROLLER_BUTTON_MAX) {
   6.295 +                case SDL_CONTROLLER_BINDING_AXIS_LEFTX_NEGATIVE:
   6.296 +                    if (!BMergeAxisBindings(iIndex)) {
   6.297 +                        SDL_strlcat(mapping, "-", SDL_arraysize(mapping));
   6.298 +                    }
   6.299 +                    pszAxisName = SDL_GameControllerGetStringForAxis(SDL_CONTROLLER_AXIS_LEFTX);
   6.300 +                    break;
   6.301 +                case SDL_CONTROLLER_BINDING_AXIS_LEFTX_POSITIVE:
   6.302 +                    SDL_strlcat(mapping, "+", SDL_arraysize(mapping));
   6.303 +                    pszAxisName = SDL_GameControllerGetStringForAxis(SDL_CONTROLLER_AXIS_LEFTX);
   6.304 +                    break;
   6.305 +                case SDL_CONTROLLER_BINDING_AXIS_LEFTY_NEGATIVE:
   6.306 +                    if (!BMergeAxisBindings(iIndex)) {
   6.307 +                        SDL_strlcat(mapping, "-", SDL_arraysize(mapping));
   6.308 +                    }
   6.309 +                    pszAxisName = SDL_GameControllerGetStringForAxis(SDL_CONTROLLER_AXIS_LEFTY);
   6.310 +                    break;
   6.311 +                case SDL_CONTROLLER_BINDING_AXIS_LEFTY_POSITIVE:
   6.312 +                    SDL_strlcat(mapping, "+", SDL_arraysize(mapping));
   6.313 +                    pszAxisName = SDL_GameControllerGetStringForAxis(SDL_CONTROLLER_AXIS_LEFTY);
   6.314 +                    break;
   6.315 +                case SDL_CONTROLLER_BINDING_AXIS_RIGHTX_NEGATIVE:
   6.316 +                    if (!BMergeAxisBindings(iIndex)) {
   6.317 +                        SDL_strlcat(mapping, "-", SDL_arraysize(mapping));
   6.318 +                    }
   6.319 +                    pszAxisName = SDL_GameControllerGetStringForAxis(SDL_CONTROLLER_AXIS_RIGHTX);
   6.320 +                    break;
   6.321 +                case SDL_CONTROLLER_BINDING_AXIS_RIGHTX_POSITIVE:
   6.322 +                    SDL_strlcat(mapping, "+", SDL_arraysize(mapping));
   6.323 +                    pszAxisName = SDL_GameControllerGetStringForAxis(SDL_CONTROLLER_AXIS_RIGHTX);
   6.324 +                    break;
   6.325 +                case SDL_CONTROLLER_BINDING_AXIS_RIGHTY_NEGATIVE:
   6.326 +                    if (!BMergeAxisBindings(iIndex)) {
   6.327 +                        SDL_strlcat(mapping, "-", SDL_arraysize(mapping));
   6.328 +                    }
   6.329 +                    pszAxisName = SDL_GameControllerGetStringForAxis(SDL_CONTROLLER_AXIS_RIGHTY);
   6.330 +                    break;
   6.331 +                case SDL_CONTROLLER_BINDING_AXIS_RIGHTY_POSITIVE:
   6.332 +                    SDL_strlcat(mapping, "+", SDL_arraysize(mapping));
   6.333 +                    pszAxisName = SDL_GameControllerGetStringForAxis(SDL_CONTROLLER_AXIS_RIGHTY);
   6.334 +                    break;
   6.335 +                case SDL_CONTROLLER_BINDING_AXIS_TRIGGERLEFT:
   6.336 +                    pszAxisName = SDL_GameControllerGetStringForAxis(SDL_CONTROLLER_AXIS_TRIGGERLEFT);
   6.337 +                    break;
   6.338 +                case SDL_CONTROLLER_BINDING_AXIS_TRIGGERRIGHT:
   6.339 +                    pszAxisName = SDL_GameControllerGetStringForAxis(SDL_CONTROLLER_AXIS_TRIGGERRIGHT);
   6.340 +                    break;
   6.341 +                }
   6.342 +                SDL_strlcat(mapping, pszAxisName, SDL_arraysize(mapping));
   6.343              }
   6.344              SDL_strlcat(mapping, ":", SDL_arraysize(mapping));
   6.345  
   6.346 @@ -450,7 +581,19 @@
   6.347                  SDL_snprintf(pszElement, sizeof(pszElement), "b%d", pBinding->value.button);
   6.348                  break;
   6.349              case SDL_CONTROLLER_BINDTYPE_AXIS:
   6.350 -                SDL_snprintf(pszElement, sizeof(pszElement), "a%d", pBinding->value.axis);
   6.351 +                if (pBinding->value.axis.axis_min == 0 && pBinding->value.axis.axis_max == SDL_JOYSTICK_AXIS_MIN) {
   6.352 +                    /* The negative half axis */
   6.353 +                    SDL_snprintf(pszElement, sizeof(pszElement), "-a%d", pBinding->value.axis.axis);
   6.354 +                } else if (pBinding->value.axis.axis_min == 0 && pBinding->value.axis.axis_max == SDL_JOYSTICK_AXIS_MAX) {
   6.355 +                    /* The positive half axis */
   6.356 +                    SDL_snprintf(pszElement, sizeof(pszElement), "+a%d", pBinding->value.axis.axis);
   6.357 +                } else {
   6.358 +                    SDL_snprintf(pszElement, sizeof(pszElement), "a%d", pBinding->value.axis.axis);
   6.359 +                    if (pBinding->value.axis.axis_min > pBinding->value.axis.axis_max) {
   6.360 +                        /* Invert the axis */
   6.361 +                        SDL_strlcat(pszElement, "~", SDL_arraysize(pszElement));
   6.362 +                    }
   6.363 +                }
   6.364                  break;
   6.365              case SDL_CONTROLLER_BINDTYPE_HAT:
   6.366                  SDL_snprintf(pszElement, sizeof(pszElement), "h%d.%d", pBinding->value.hat.hat, pBinding->value.hat.hat_mask);
   6.367 @@ -467,6 +610,9 @@
   6.368          /* Print to stdout as well so the user can cat the output somewhere */
   6.369          printf("%s\n", mapping);
   6.370      }
   6.371 +
   6.372 +    SDL_free(s_arrAxisState);
   6.373 +    s_arrAxisState = NULL;
   6.374      
   6.375      SDL_DestroyRenderer(screen);
   6.376      SDL_DestroyWindow(window);
     7.1 --- a/test/testgamecontroller.c	Mon Dec 26 23:02:14 2016 -0500
     7.2 +++ b/test/testgamecontroller.c	Tue Dec 27 01:39:07 2016 -0800
     7.3 @@ -53,12 +53,12 @@
     7.4  
     7.5  /* This is indexed by SDL_GameControllerAxis. */
     7.6  static const struct { int x; int y; double angle; } axis_positions[] = {
     7.7 -    {75,  154, 0.0},  /* LEFTX */
     7.8 -    {75,  154, 90.0},  /* LEFTY */
     7.9 -    {305, 230, 0.0},  /* RIGHTX */
    7.10 -    {305, 230, 90.0},  /* RIGHTY */
    7.11 -    {91, 0, 90.0},     /* TRIGGERLEFT */
    7.12 -    {375, 0, 90.0},    /* TRIGGERRIGHT */
    7.13 +    {74,  153, 270.0},  /* LEFTX */
    7.14 +    {74,  153, 0.0},  /* LEFTY */
    7.15 +    {306, 231, 270.0},  /* RIGHTX */
    7.16 +    {306, 231, 0.0},  /* RIGHTY */
    7.17 +    {91, -20, 0.0},     /* TRIGGERLEFT */
    7.18 +    {375, -20, 0.0},    /* TRIGGERRIGHT */
    7.19  };
    7.20  
    7.21  SDL_Renderer *screen = NULL;
    7.22 @@ -80,10 +80,6 @@
    7.23          if (transparent) {
    7.24              if (temp->format->BytesPerPixel == 1) {
    7.25                  SDL_SetColorKey(temp, SDL_TRUE, *(Uint8 *)temp->pixels);
    7.26 -            } else {
    7.27 -                SDL_assert(!temp->format->palette);
    7.28 -                SDL_assert(temp->format->BitsPerPixel == 24);
    7.29 -                SDL_SetColorKey(temp, SDL_TRUE, (*(Uint32 *)temp->pixels) & 0x00FFFFFF);
    7.30              }
    7.31          }
    7.32  
    7.33 @@ -112,6 +108,13 @@
    7.34  
    7.35      while (SDL_PollEvent(&event)) {
    7.36          switch (event.type) {
    7.37 +        case SDL_CONTROLLERAXISMOTION:
    7.38 +            SDL_Log("Controller axis %s changed to %d\n", SDL_GameControllerGetStringForAxis(event.caxis.axis), event.caxis.value);
    7.39 +            break;
    7.40 +        case SDL_CONTROLLERBUTTONDOWN:
    7.41 +        case SDL_CONTROLLERBUTTONUP:
    7.42 +            SDL_Log("Controller button %s %s\n", SDL_GameControllerGetStringForButton(event.cbutton.button), event.cbutton.state ? "pressed" : "released");
    7.43 +            break;
    7.44          case SDL_KEYDOWN:
    7.45              if (event.key.keysym.sym != SDLK_ESCAPE) {
    7.46                  break;