diff --git a/include/SDL3/SDL_hints.h b/include/SDL3/SDL_hints.h index ba98e74f0f..cd5b1c1a96 100644 --- a/include/SDL3/SDL_hints.h +++ b/include/SDL3/SDL_hints.h @@ -2191,6 +2191,28 @@ extern "C" { */ #define SDL_HINT_JOYSTICK_ZERO_CENTERED_DEVICES "SDL_JOYSTICK_ZERO_CENTERED_DEVICES" +/** + * A variable containing a list of devices and their desired number of haptic + * (force feedback) enabled axis. + * + * The format of the string is a comma separated list of USB VID/PID pairs in + * hexadecimal form plus the number of desired axes, e.g. + * + * `0xAAAA/0xBBBB/1,0xCCCC/0xDDDD/3` + * + * This hint supports a "wildcard" device that will set the number of haptic + * axes on all initialized haptic devices which were not defined explicitly + * in this hint. + * + * `0xFFFF/0xFFFF/1` + * + * This hint should be set before a controller is opened. The number of + * haptic axes won't exceed the number of real axes found on the device. + * + * \since This hint is available since SDL 3.2.5. +*/ +#define SDL_HINT_JOYSTICK_HAPTIC_AXES "SDL_JOYSTICK_HAPTIC_AXES" + /** * A variable that controls keycode representation in keyboard events. * diff --git a/src/haptic/SDL_haptic.c b/src/haptic/SDL_haptic.c index 671a702736..1c11db6864 100644 --- a/src/haptic/SDL_haptic.c +++ b/src/haptic/SDL_haptic.c @@ -23,6 +23,84 @@ #include "SDL_syshaptic.h" #include "SDL_haptic_c.h" #include "../joystick/SDL_joystick_c.h" // For SDL_IsJoystickValid +#include "../SDL_hints_c.h" + +typedef struct SDL_Haptic_VIDPID_Naxes { + Uint16 vid; + Uint16 pid; + Uint16 naxes; +} SDL_Haptic_VIDPID_Naxes; + +static void SDL_Haptic_Load_Axes_List(SDL_Haptic_VIDPID_Naxes **entries, int *num_entries) +{ + SDL_Haptic_VIDPID_Naxes entry; + const char *spot; + int length = 0; + + spot = SDL_GetHint(SDL_HINT_JOYSTICK_HAPTIC_AXES); + if (!spot) + return; + + while (SDL_sscanf(spot, "0x%hx/0x%hx/%hu%n", &entry.vid, &entry.pid, &entry.naxes, &length) == 3) { + SDL_assert(length > 0); + spot += length; + length = 0; + + if ((*num_entries % 8) == 0) { + int new_max = *num_entries + 8; + SDL_Haptic_VIDPID_Naxes *new_entries = + (SDL_Haptic_VIDPID_Naxes *)SDL_realloc(*entries, new_max * sizeof(**entries)); + + // Out of memory, go with what we have already + if (!new_entries) + break; + + *entries = new_entries; + } + (*entries)[(*num_entries)++] = entry; + + if (spot[0] == ',') + spot++; + } +} + +// /* Return -1 if not found */ +static int SDL_Haptic_Naxes_List_Index(struct SDL_Haptic_VIDPID_Naxes *entries, int num_entries, Uint16 vid, Uint16 pid) +{ + if (!entries) + return -1; + + int i; + for (i = 0; i < num_entries; ++i) { + if (entries[i].vid == vid && entries[i].pid == pid) + return i; + } + + return -1; +} + +// Check if device needs a custom number of naxes +static int SDL_Haptic_Get_Naxes(Uint16 vid, Uint16 pid) +{ + int num_entries = 0, index = 0, naxes = -1; + SDL_Haptic_VIDPID_Naxes *naxes_list = NULL; + + SDL_Haptic_Load_Axes_List(&naxes_list, &num_entries); + if (!num_entries || !naxes_list) + return -1; + + // Perform "wildcard" pass + index = SDL_Haptic_Naxes_List_Index(naxes_list, num_entries, 0xffff, 0xffff); + if (index >= 0) + naxes = naxes_list[index].naxes; + + index = SDL_Haptic_Naxes_List_Index(naxes_list, num_entries, vid, pid); + if (index >= 0) + naxes = naxes_list[index].naxes; + + SDL_free(naxes_list); + return naxes; +} static SDL_Haptic *SDL_haptics = NULL; @@ -282,6 +360,19 @@ SDL_Haptic *SDL_OpenHapticFromJoystick(SDL_Joystick *joystick) } SDL_UnlockJoysticks(); + // Check if custom number of haptic axes was defined + Uint16 vid = SDL_GetJoystickVendor(joystick); + Uint16 pid = SDL_GetJoystickProduct(joystick); + int general_axes = SDL_GetNumJoystickAxes(joystick); + + int naxes = SDL_Haptic_Get_Naxes(vid, pid); + if (naxes > 0) + haptic->naxes = naxes; + + // Limit to the actual number of axes found on the device + if (general_axes >= 0 && naxes > general_axes) + haptic->naxes = general_axes; + // Add haptic to list ++haptic->ref_count; // Link the haptic in the list