#include "../SDL_internal.h"
#include "SDL.h"
#include "SDL_atomic.h"
#include "SDL_events.h"
#include "SDL_sysjoystick.h"
#include "SDL_hints.h"
#include "../SDL_hints_c.h"
#if !SDL_EVENTS_DISABLED
#include "../events/SDL_events_c.h"
#endif
#include "../video/SDL_sysvideo.h"
#include "hidapi/SDL_hidapijoystick_c.h"
#include "controller_type.h"
#if defined(__WIN32__) || defined(__WINGDK__)
#include "../core/windows/SDL_windows.h"
#undef UNICODE
#include <tlhelp32.h>
#endif
#if SDL_JOYSTICK_VIRTUAL
#include "./virtual/SDL_virtualjoystick_c.h"
#endif
static SDL_JoystickDriver *SDL_joystick_drivers[] = {
#ifdef SDL_JOYSTICK_HIDAPI
&SDL_HIDAPI_JoystickDriver,
#endif
#ifdef SDL_JOYSTICK_RAWINPUT
&SDL_RAWINPUT_JoystickDriver,
#endif
#if defined(SDL_JOYSTICK_DINPUT) || defined(SDL_JOYSTICK_XINPUT)
&SDL_WINDOWS_JoystickDriver,
#endif
#if defined(SDL_JOYSTICK_WGI)
&SDL_WGI_JoystickDriver,
#endif
#if defined(SDL_JOYSTICK_WINMM)
&SDL_WINMM_JoystickDriver,
#endif
#ifdef SDL_JOYSTICK_LINUX
&SDL_LINUX_JoystickDriver,
#endif
#ifdef SDL_JOYSTICK_IOKIT
&SDL_DARWIN_JoystickDriver,
#endif
#if (defined(__MACOSX__) || defined(__IPHONEOS__) || defined(__TVOS__)) && !defined(SDL_JOYSTICK_DISABLED)
&SDL_IOS_JoystickDriver,
#endif
#ifdef SDL_JOYSTICK_ANDROID
&SDL_ANDROID_JoystickDriver,
#endif
#ifdef SDL_JOYSTICK_EMSCRIPTEN
&SDL_EMSCRIPTEN_JoystickDriver,
#endif
#ifdef SDL_JOYSTICK_HAIKU
&SDL_HAIKU_JoystickDriver,
#endif
#ifdef SDL_JOYSTICK_USBHID
&SDL_BSD_JoystickDriver,
#endif
#ifdef SDL_JOYSTICK_OS2
&SDL_OS2_JoystickDriver,
#endif
#ifdef SDL_JOYSTICK_PS2
&SDL_PS2_JoystickDriver,
#endif
#ifdef SDL_JOYSTICK_PSP
&SDL_PSP_JoystickDriver,
#endif
#ifdef SDL_JOYSTICK_VIRTUAL
&SDL_VIRTUAL_JoystickDriver,
#endif
#ifdef SDL_JOYSTICK_VITA
&SDL_VITA_JoystickDriver,
#endif
#ifdef SDL_JOYSTICK_N3DS
&SDL_N3DS_JoystickDriver
#endif
#if defined(SDL_JOYSTICK_DUMMY) || defined(SDL_JOYSTICK_DISABLED)
&SDL_DUMMY_JoystickDriver
#endif
};
static SDL_bool SDL_joysticks_initialized;
static SDL_bool SDL_joysticks_quitting = SDL_FALSE;
static SDL_Joystick *SDL_joysticks = NULL;
static SDL_mutex *SDL_joystick_lock = NULL;
static int SDL_joysticks_locked;
static SDL_atomic_t SDL_next_joystick_instance_id;
static int SDL_joystick_player_count = 0;
static SDL_JoystickID *SDL_joystick_players = NULL;
static SDL_bool SDL_joystick_allows_background_events = SDL_FALSE;
static char joystick_magic;
#define CHECK_JOYSTICK_MAGIC(joystick, retval) \
if (!joystick || joystick->magic != &joystick_magic) { \
SDL_InvalidParamError("joystick"); \
return retval; \
}
SDL_bool
SDL_JoysticksInitialized(void)
{
return SDL_joysticks_initialized;
}
SDL_bool
SDL_JoysticksQuitting(void)
{
return SDL_joysticks_quitting;
}
void
SDL_LockJoysticks(void)
{
if (SDL_joystick_lock) {
SDL_LockMutex(SDL_joystick_lock);
}
++SDL_joysticks_locked;
}
void
SDL_UnlockJoysticks(void)
{
--SDL_joysticks_locked;
if (SDL_joystick_lock) {
SDL_UnlockMutex(SDL_joystick_lock);
if (!SDL_joysticks_locked &&
!SDL_joysticks_initialized) {
SDL_DestroyMutex(SDL_joystick_lock);
SDL_joystick_lock = NULL;
}
}
}
SDL_bool
SDL_JoysticksLocked(void)
{
return (SDL_joysticks_locked > 0) ? SDL_TRUE : SDL_FALSE;
}
void
SDL_AssertJoysticksLocked(void)
{
SDL_assert(SDL_JoysticksLocked());
}
static SDL_bool
SDL_GetDriverAndJoystickIndex(int device_index, SDL_JoystickDriver **driver, int *driver_index)
{
int i, num_joysticks, total_joysticks = 0;
SDL_AssertJoysticksLocked();
if (device_index >= 0) {
for (i = 0; i < SDL_arraysize(SDL_joystick_drivers); ++i) {
num_joysticks = SDL_joystick_drivers[i]->GetCount();
if (device_index < num_joysticks) {
*driver = SDL_joystick_drivers[i];
*driver_index = device_index;
return SDL_TRUE;
}
device_index -= num_joysticks;
total_joysticks += num_joysticks;
}
}
SDL_SetError("There are %d joysticks available", total_joysticks);
return SDL_FALSE;
}
static int
SDL_FindFreePlayerIndex()
{
int player_index;
SDL_AssertJoysticksLocked();
for (player_index = 0; player_index < SDL_joystick_player_count; ++player_index) {
if (SDL_joystick_players[player_index] == -1) {
return player_index;
}
}
return player_index;
}
static int
SDL_GetPlayerIndexForJoystickID(SDL_JoystickID instance_id)
{
int player_index;
SDL_AssertJoysticksLocked();
for (player_index = 0; player_index < SDL_joystick_player_count; ++player_index) {
if (instance_id == SDL_joystick_players[player_index]) {
break;
}
}
if (player_index == SDL_joystick_player_count) {
player_index = -1;
}
return player_index;
}
static SDL_JoystickID
SDL_GetJoystickIDForPlayerIndex(int player_index)
{
SDL_AssertJoysticksLocked();
if (player_index < 0 || player_index >= SDL_joystick_player_count) {
return -1;
}
return SDL_joystick_players[player_index];
}
static SDL_bool
SDL_SetJoystickIDForPlayerIndex(int player_index, SDL_JoystickID instance_id)
{
SDL_JoystickID existing_instance = SDL_GetJoystickIDForPlayerIndex(player_index);
SDL_JoystickDriver *driver;
int device_index;
int existing_player_index;
SDL_AssertJoysticksLocked();
if (player_index >= SDL_joystick_player_count) {
SDL_JoystickID *new_players = (SDL_JoystickID *)SDL_realloc(SDL_joystick_players, (player_index + 1)*sizeof(*SDL_joystick_players));
if (!new_players) {
SDL_OutOfMemory();
return SDL_FALSE;
}
SDL_joystick_players = new_players;
SDL_memset(&SDL_joystick_players[SDL_joystick_player_count], 0xFF, (player_index - SDL_joystick_player_count + 1) * sizeof(SDL_joystick_players[0]));
SDL_joystick_player_count = player_index + 1;
} else if (player_index >= 0 && SDL_joystick_players[player_index] == instance_id) {
return SDL_TRUE;
}
existing_player_index = SDL_GetPlayerIndexForJoystickID(instance_id);
if (existing_player_index >= 0) {
SDL_joystick_players[existing_player_index] = -1;
}
if (player_index >= 0) {
SDL_joystick_players[player_index] = instance_id;
}
device_index = SDL_JoystickGetDeviceIndexFromInstanceID(instance_id);
if (SDL_GetDriverAndJoystickIndex(device_index, &driver, &device_index)) {
driver->SetDevicePlayerIndex(device_index, player_index);
}
if (existing_instance >= 0) {
SDL_SetJoystickIDForPlayerIndex(SDL_FindFreePlayerIndex(), existing_instance);
}
return SDL_TRUE;
}
static void SDLCALL
SDL_JoystickAllowBackgroundEventsChanged(void *userdata, const char *name, const char *oldValue, const char *hint)
{
if (SDL_GetStringBoolean(hint, SDL_FALSE)) {
SDL_joystick_allows_background_events = SDL_TRUE;
} else {
SDL_joystick_allows_background_events = SDL_FALSE;
}
}
int
SDL_JoystickInit(void)
{
int i, status;
if (!SDL_joystick_lock) {
SDL_joystick_lock = SDL_CreateMutex();
}
#if !SDL_EVENTS_DISABLED
if (SDL_InitSubSystem(SDL_INIT_EVENTS) < 0) {
return -1;
}
#endif
SDL_LockJoysticks();
SDL_joysticks_initialized = SDL_TRUE;
SDL_GameControllerInitMappings();
SDL_AddHintCallback(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS,
SDL_JoystickAllowBackgroundEventsChanged, NULL);
status = -1;
for (i = 0; i < SDL_arraysize(SDL_joystick_drivers); ++i) {
if (SDL_joystick_drivers[i]->Init() >= 0) {
status = 0;
}
}
SDL_UnlockJoysticks();
if (status < 0) {
SDL_JoystickQuit();
}
return status;
}
int
SDL_NumJoysticks(void)
{
int i, total_joysticks = 0;
SDL_LockJoysticks();
for (i = 0; i < SDL_arraysize(SDL_joystick_drivers); ++i) {
total_joysticks += SDL_joystick_drivers[i]->GetCount();
}
SDL_UnlockJoysticks();
return total_joysticks;
}
SDL_JoystickID SDL_GetNextJoystickInstanceID()
{
return SDL_AtomicIncRef(&SDL_next_joystick_instance_id);
}
const char *
SDL_JoystickNameForIndex(int device_index)
{
SDL_JoystickDriver *driver;
const char *name = NULL;
SDL_LockJoysticks();
if (SDL_GetDriverAndJoystickIndex(device_index, &driver, &device_index)) {
name = driver->GetDeviceName(device_index);
}
SDL_UnlockJoysticks();
return name;
}
const char *
SDL_JoystickPathForIndex(int device_index)
{
SDL_JoystickDriver *driver;
const char *path = NULL;
SDL_LockJoysticks();
if (SDL_GetDriverAndJoystickIndex(device_index, &driver, &device_index)) {
path = driver->GetDevicePath(device_index);
}
SDL_UnlockJoysticks();
if (!path) {
SDL_Unsupported();
}
return path;
}
int
SDL_JoystickGetDevicePlayerIndex(int device_index)
{
int player_index;
SDL_LockJoysticks();
player_index = SDL_GetPlayerIndexForJoystickID(SDL_JoystickGetDeviceInstanceID(device_index));
SDL_UnlockJoysticks();
return player_index;
}
static SDL_bool
SDL_JoystickAxesCenteredAtZero(SDL_Joystick *joystick)
{
#ifdef __WINRT__
return SDL_TRUE;
#else
static Uint32 zero_centered_joysticks[] = {
MAKE_VIDPID(0x0e8f, 0x3013),
MAKE_VIDPID(0x05a0, 0x3232),
};
int i;
Uint32 id = MAKE_VIDPID(SDL_JoystickGetVendor(joystick),
SDL_JoystickGetProduct(joystick));
if (joystick->naxes == 2) {
return SDL_TRUE;
}
for (i = 0; i < SDL_arraysize(zero_centered_joysticks); ++i) {
if (id == zero_centered_joysticks[i]) {
return SDL_TRUE;
}
}
return SDL_FALSE;
#endif
}
SDL_Joystick *
SDL_JoystickOpen(int device_index)
{
SDL_JoystickDriver *driver;
SDL_JoystickID instance_id;
SDL_Joystick *joystick;
SDL_Joystick *joysticklist;
const char *joystickname = NULL;
const char *joystickpath = NULL;
SDL_JoystickPowerLevel initial_power_level;
SDL_LockJoysticks();
if (!SDL_GetDriverAndJoystickIndex(device_index, &driver, &device_index)) {
SDL_UnlockJoysticks();
return NULL;
}
joysticklist = SDL_joysticks;
instance_id = driver->GetDeviceInstanceID(device_index);
while (joysticklist) {
if (instance_id == joysticklist->instance_id) {
joystick = joysticklist;
++joystick->ref_count;
SDL_UnlockJoysticks();
return joystick;
}
joysticklist = joysticklist->next;
}
joystick = (SDL_Joystick *) SDL_calloc(sizeof(*joystick), 1);
if (joystick == NULL) {
SDL_OutOfMemory();
SDL_UnlockJoysticks();
return NULL;
}
joystick->magic = &joystick_magic;
joystick->driver = driver;
joystick->instance_id = instance_id;
joystick->attached = SDL_TRUE;
joystick->epowerlevel = SDL_JOYSTICK_POWER_UNKNOWN;
joystick->led_expiration = SDL_GetTicks();
if (driver->Open(joystick, device_index) < 0) {
SDL_free(joystick);
SDL_UnlockJoysticks();
return NULL;
}
joystickname = driver->GetDeviceName(device_index);
if (joystickname) {
joystick->name = SDL_strdup(joystickname);
} else {
joystick->name = NULL;
}
joystickpath = driver->GetDevicePath(device_index);
if (joystickpath) {
joystick->path = SDL_strdup(joystickpath);
} else {
joystick->path = NULL;
}
joystick->guid = driver->GetDeviceGUID(device_index);
if (joystick->naxes > 0) {
joystick->axes = (SDL_JoystickAxisInfo *) SDL_calloc(joystick->naxes, sizeof(SDL_JoystickAxisInfo));
}
if (joystick->nhats > 0) {
joystick->hats = (Uint8 *) SDL_calloc(joystick->nhats, sizeof(Uint8));
}
if (joystick->nballs > 0) {
joystick->balls = (struct balldelta *) SDL_calloc(joystick->nballs, sizeof(*joystick->balls));
}
if (joystick->nbuttons > 0) {
joystick->buttons = (Uint8 *) SDL_calloc(joystick->nbuttons, sizeof(Uint8));
}
if (((joystick->naxes > 0) && !joystick->axes)
|| ((joystick->nhats > 0) && !joystick->hats)
|| ((joystick->nballs > 0) && !joystick->balls)
|| ((joystick->nbuttons > 0) && !joystick->buttons)) {
SDL_OutOfMemory();
SDL_JoystickClose(joystick);
SDL_UnlockJoysticks();
return NULL;
}
if (SDL_JoystickAxesCenteredAtZero(joystick)) {
int i;
for (i = 0; i < joystick->naxes; ++i) {
joystick->axes[i].has_initial_value = SDL_TRUE;
}
}
joystick->is_game_controller = SDL_IsGameController(device_index);
++joystick->ref_count;
joystick->next = SDL_joysticks;
SDL_joysticks = joystick;
initial_power_level = joystick->epowerlevel;
joystick->epowerlevel = SDL_JOYSTICK_POWER_UNKNOWN;
SDL_PrivateJoystickBatteryLevel(joystick, initial_power_level);
driver->Update(joystick);
SDL_UnlockJoysticks();
return joystick;
}
int
SDL_JoystickAttachVirtual(SDL_JoystickType type,
int naxes, int nbuttons, int nhats)
{
SDL_VirtualJoystickDesc desc;
SDL_zero(desc);
desc.version = SDL_VIRTUAL_JOYSTICK_DESC_VERSION;
desc.type = (Uint16)type;
desc.naxes = (Uint16)naxes;
desc.nbuttons = (Uint16)nbuttons;
desc.nhats = (Uint16)nhats;
return SDL_JoystickAttachVirtualEx(&desc);
}
int
SDL_JoystickAttachVirtualEx(const SDL_VirtualJoystickDesc *desc)
{
#if SDL_JOYSTICK_VIRTUAL
int result;
SDL_LockJoysticks();
result = SDL_JoystickAttachVirtualInner(desc);
SDL_UnlockJoysticks();
return result;
#else
return SDL_SetError("SDL not built with virtual-joystick support");
#endif
}
int
SDL_JoystickDetachVirtual(int device_index)
{
#if SDL_JOYSTICK_VIRTUAL
SDL_JoystickDriver *driver;
SDL_LockJoysticks();
if (SDL_GetDriverAndJoystickIndex(device_index, &driver, &device_index)) {
if (driver == &SDL_VIRTUAL_JoystickDriver) {
const int result = SDL_JoystickDetachVirtualInner(device_index);
SDL_UnlockJoysticks();
return result;
}
}
SDL_UnlockJoysticks();
return SDL_SetError("Virtual joystick not found at provided index");
#else
return SDL_SetError("SDL not built with virtual-joystick support");
#endif
}
SDL_bool
SDL_JoystickIsVirtual(int device_index)
{
#if SDL_JOYSTICK_VIRTUAL
SDL_JoystickDriver *driver;
int driver_device_index;
SDL_bool is_virtual = SDL_FALSE;
SDL_LockJoysticks();
if (SDL_GetDriverAndJoystickIndex(device_index, &driver, &driver_device_index)) {
if (driver == &SDL_VIRTUAL_JoystickDriver) {
is_virtual = SDL_TRUE;
}
}
SDL_UnlockJoysticks();
return is_virtual;
#else
return SDL_FALSE;
#endif
}
int
SDL_JoystickSetVirtualAxis(SDL_Joystick *joystick, int axis, Sint16 value)
{
CHECK_JOYSTICK_MAGIC(joystick, -1);
#if SDL_JOYSTICK_VIRTUAL
return SDL_JoystickSetVirtualAxisInner(joystick, axis, value);
#else
return SDL_SetError("SDL not built with virtual-joystick support");
#endif
}
int
SDL_JoystickSetVirtualButton(SDL_Joystick *joystick, int button, Uint8 value)
{
CHECK_JOYSTICK_MAGIC(joystick, -1);
#if SDL_JOYSTICK_VIRTUAL
return SDL_JoystickSetVirtualButtonInner(joystick, button, value);
#else
return SDL_SetError("SDL not built with virtual-joystick support");
#endif
}
int
SDL_JoystickSetVirtualHat(SDL_Joystick *joystick, int hat, Uint8 value)
{
CHECK_JOYSTICK_MAGIC(joystick, -1);
#if SDL_JOYSTICK_VIRTUAL
return SDL_JoystickSetVirtualHatInner(joystick, hat, value);
#else
return SDL_SetError("SDL not built with virtual-joystick support");
#endif
}
SDL_bool
SDL_PrivateJoystickValid(SDL_Joystick *joystick)
{
CHECK_JOYSTICK_MAGIC(joystick, SDL_FALSE);
return SDL_TRUE;
}
SDL_bool
SDL_PrivateJoystickGetAutoGamepadMapping(int device_index, SDL_GamepadMapping * out)
{
SDL_JoystickDriver *driver;
SDL_bool is_ok = SDL_FALSE;
SDL_LockJoysticks();
if (SDL_GetDriverAndJoystickIndex(device_index, &driver, &device_index)) {
is_ok = driver->GetGamepadMapping(device_index, out);
}
SDL_UnlockJoysticks();
return is_ok;
}
int
SDL_JoystickNumAxes(SDL_Joystick *joystick)
{
CHECK_JOYSTICK_MAGIC(joystick, -1);
return joystick->naxes;
}
int
SDL_JoystickNumHats(SDL_Joystick *joystick)
{
CHECK_JOYSTICK_MAGIC(joystick, -1);
return joystick->nhats;
}
int
SDL_JoystickNumBalls(SDL_Joystick *joystick)
{
CHECK_JOYSTICK_MAGIC(joystick, -1);
return joystick->nballs;
}
int
SDL_JoystickNumButtons(SDL_Joystick *joystick)
{
CHECK_JOYSTICK_MAGIC(joystick, -1);
return joystick->nbuttons;
}
Sint16
SDL_JoystickGetAxis(SDL_Joystick *joystick, int axis)
{
Sint16 state;
CHECK_JOYSTICK_MAGIC(joystick, 0);
if (axis < joystick->naxes) {
state = joystick->axes[axis].value;
} else {
SDL_SetError("Joystick only has %d axes", joystick->naxes);
state = 0;
}
return state;
}
SDL_bool
SDL_JoystickGetAxisInitialState(SDL_Joystick *joystick, int axis, Sint16 *state)
{
CHECK_JOYSTICK_MAGIC(joystick, SDL_FALSE);
if (axis >= joystick->naxes) {
SDL_SetError("Joystick only has %d axes", joystick->naxes);
return SDL_FALSE;
}
if (state) {
*state = joystick->axes[axis].initial_value;
}
return joystick->axes[axis].has_initial_value;
}
Uint8
SDL_JoystickGetHat(SDL_Joystick *joystick, int hat)
{
Uint8 state;
CHECK_JOYSTICK_MAGIC(joystick, 0);
if (hat < joystick->nhats) {
state = joystick->hats[hat];
} else {
SDL_SetError("Joystick only has %d hats", joystick->nhats);
state = 0;
}
return state;
}
int
SDL_JoystickGetBall(SDL_Joystick *joystick, int ball, int *dx, int *dy)
{
int retval;
CHECK_JOYSTICK_MAGIC(joystick, -1);
retval = 0;
if (ball < joystick->nballs) {
if (dx) {
*dx = joystick->balls[ball].dx;
}
if (dy) {
*dy = joystick->balls[ball].dy;
}
joystick->balls[ball].dx = 0;
joystick->balls[ball].dy = 0;
} else {
return SDL_SetError("Joystick only has %d balls", joystick->nballs);
}
return retval;
}
Uint8
SDL_JoystickGetButton(SDL_Joystick *joystick, int button)
{
Uint8 state;
CHECK_JOYSTICK_MAGIC(joystick, 0);
if (button < joystick->nbuttons) {
state = joystick->buttons[button];
} else {
SDL_SetError("Joystick only has %d buttons", joystick->nbuttons);
state = 0;
}
return state;
}
SDL_bool
SDL_JoystickGetAttached(SDL_Joystick *joystick)
{
CHECK_JOYSTICK_MAGIC(joystick, SDL_FALSE);
return joystick->attached;
}
SDL_JoystickID
SDL_JoystickInstanceID(SDL_Joystick *joystick)
{
CHECK_JOYSTICK_MAGIC(joystick, -1);
return joystick->instance_id;
}
SDL_Joystick *
SDL_JoystickFromInstanceID(SDL_JoystickID instance_id)
{
SDL_Joystick *joystick;
SDL_LockJoysticks();
for (joystick = SDL_joysticks; joystick; joystick = joystick->next) {
if (joystick->instance_id == instance_id) {
break;
}
}
SDL_UnlockJoysticks();
return joystick;
}
SDL_Joystick *
SDL_JoystickFromPlayerIndex(int player_index)
{
SDL_JoystickID instance_id;
SDL_Joystick *joystick;
SDL_LockJoysticks();
instance_id = SDL_GetJoystickIDForPlayerIndex(player_index);
for (joystick = SDL_joysticks; joystick; joystick = joystick->next) {
if (joystick->instance_id == instance_id) {
break;
}
}
SDL_UnlockJoysticks();
return joystick;
}
const char *
SDL_JoystickName(SDL_Joystick *joystick)
{
CHECK_JOYSTICK_MAGIC(joystick, NULL);
return joystick->name;
}
const char *
SDL_JoystickPath(SDL_Joystick *joystick)
{
CHECK_JOYSTICK_MAGIC(joystick, NULL);
if (!joystick->path) {
SDL_Unsupported();
return NULL;
}
return joystick->path;
}
int
SDL_JoystickGetPlayerIndex(SDL_Joystick *joystick)
{
int player_index;
CHECK_JOYSTICK_MAGIC(joystick, -1);
SDL_LockJoysticks();
player_index = SDL_GetPlayerIndexForJoystickID(joystick->instance_id);
SDL_UnlockJoysticks();
return player_index;
}
void
SDL_JoystickSetPlayerIndex(SDL_Joystick *joystick, int player_index)
{
CHECK_JOYSTICK_MAGIC(joystick, );
SDL_LockJoysticks();
SDL_SetJoystickIDForPlayerIndex(player_index, joystick->instance_id);
SDL_UnlockJoysticks();
}
int
SDL_JoystickRumble(SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms)
{
int result;
CHECK_JOYSTICK_MAGIC(joystick, -1);
SDL_LockJoysticks();
if (low_frequency_rumble == joystick->low_frequency_rumble &&
high_frequency_rumble == joystick->high_frequency_rumble) {
result = 0;
} else {
result = joystick->driver->Rumble(joystick, low_frequency_rumble, high_frequency_rumble);
joystick->rumble_resend = SDL_GetTicks() + SDL_RUMBLE_RESEND_MS;
if (!joystick->rumble_resend) {
joystick->rumble_resend = 1;
}
}
if (result == 0) {
joystick->low_frequency_rumble = low_frequency_rumble;
joystick->high_frequency_rumble = high_frequency_rumble;
if ((low_frequency_rumble || high_frequency_rumble) && duration_ms) {
joystick->rumble_expiration = SDL_GetTicks() + SDL_min(duration_ms, SDL_MAX_RUMBLE_DURATION_MS);
if (!joystick->rumble_expiration) {
joystick->rumble_expiration = 1;
}
} else {
joystick->rumble_expiration = 0;
joystick->rumble_resend = 0;
}
}
SDL_UnlockJoysticks();
return result;
}
int
SDL_JoystickRumbleTriggers(SDL_Joystick *joystick, Uint16 left_rumble, Uint16 right_rumble, Uint32 duration_ms)
{
int result;
CHECK_JOYSTICK_MAGIC(joystick, -1);
SDL_LockJoysticks();
if (left_rumble == joystick->left_trigger_rumble && right_rumble == joystick->right_trigger_rumble) {
result = 0;
} else {
result = joystick->driver->RumbleTriggers(joystick, left_rumble, right_rumble);
}
if (result == 0) {
joystick->left_trigger_rumble = left_rumble;
joystick->right_trigger_rumble = right_rumble;
if ((left_rumble || right_rumble) && duration_ms) {
joystick->trigger_rumble_expiration = SDL_GetTicks() + SDL_min(duration_ms, SDL_MAX_RUMBLE_DURATION_MS);
if (!joystick->trigger_rumble_expiration) {
joystick->trigger_rumble_expiration = 1;
}
} else {
joystick->trigger_rumble_expiration = 0;
}
}
SDL_UnlockJoysticks();
return result;
}
SDL_bool
SDL_JoystickHasLED(SDL_Joystick *joystick)
{
SDL_bool result;
CHECK_JOYSTICK_MAGIC(joystick, SDL_FALSE);
SDL_LockJoysticks();
result = (joystick->driver->GetCapabilities(joystick) & SDL_JOYCAP_LED) != 0;
SDL_UnlockJoysticks();
return result;
}
SDL_bool
SDL_JoystickHasRumble(SDL_Joystick *joystick)
{
SDL_bool result;
CHECK_JOYSTICK_MAGIC(joystick, SDL_FALSE);
SDL_LockJoysticks();
result = (joystick->driver->GetCapabilities(joystick) & SDL_JOYCAP_RUMBLE) != 0;
SDL_UnlockJoysticks();
return result;
}
SDL_bool
SDL_JoystickHasRumbleTriggers(SDL_Joystick *joystick)
{
SDL_bool result;
CHECK_JOYSTICK_MAGIC(joystick, SDL_FALSE);
SDL_LockJoysticks();
result = (joystick->driver->GetCapabilities(joystick) & SDL_JOYCAP_RUMBLE_TRIGGERS) != 0;
SDL_UnlockJoysticks();
return result;
}
int
SDL_JoystickSetLED(SDL_Joystick *joystick, Uint8 red, Uint8 green, Uint8 blue)
{
int result;
SDL_bool isfreshvalue;
CHECK_JOYSTICK_MAGIC(joystick, -1);
SDL_LockJoysticks();
isfreshvalue = red != joystick->led_red ||
green != joystick->led_green ||
blue != joystick->led_blue;
if (isfreshvalue || SDL_TICKS_PASSED(SDL_GetTicks(), joystick->led_expiration)) {
result = joystick->driver->SetLED(joystick, red, green, blue);
joystick->led_expiration = SDL_GetTicks() + SDL_LED_MIN_REPEAT_MS;
} else {
result = 0;
}
joystick->led_red = red;
joystick->led_green = green;
joystick->led_blue = blue;
SDL_UnlockJoysticks();
return result;
}
int
SDL_JoystickSendEffect(SDL_Joystick *joystick, const void *data, int size)
{
int result;
CHECK_JOYSTICK_MAGIC(joystick, -1);
SDL_LockJoysticks();
result = joystick->driver->SendEffect(joystick, data, size);
SDL_UnlockJoysticks();
return result;
}
void
SDL_JoystickClose(SDL_Joystick *joystick)
{
SDL_Joystick *joysticklist;
SDL_Joystick *joysticklistprev;
int i;
CHECK_JOYSTICK_MAGIC(joystick, );
SDL_LockJoysticks();
if (--joystick->ref_count > 0) {
SDL_UnlockJoysticks();
return;
}
if (joystick->rumble_expiration) {
SDL_JoystickRumble(joystick, 0, 0, 0);
}
if (joystick->trigger_rumble_expiration) {
SDL_JoystickRumbleTriggers(joystick, 0, 0, 0);
}
joystick->driver->Close(joystick);
joystick->hwdata = NULL;
joystick->magic = NULL;
joysticklist = SDL_joysticks;
joysticklistprev = NULL;
while (joysticklist) {
if (joystick == joysticklist) {
if (joysticklistprev) {
joysticklistprev->next = joysticklist->next;
} else {
SDL_joysticks = joystick->next;
}
break;
}
joysticklistprev = joysticklist;
joysticklist = joysticklist->next;
}
SDL_free(joystick->name);
SDL_free(joystick->path);
SDL_free(joystick->serial);
SDL_free(joystick->axes);
SDL_free(joystick->hats);
SDL_free(joystick->balls);
SDL_free(joystick->buttons);
for (i = 0; i < joystick->ntouchpads; i++) {
SDL_JoystickTouchpadInfo *touchpad = &joystick->touchpads[i];
SDL_free(touchpad->fingers);
}
SDL_free(joystick->touchpads);
SDL_free(joystick->sensors);
SDL_free(joystick);
SDL_UnlockJoysticks();
}
void
SDL_JoystickQuit(void)
{
int i;
SDL_LockJoysticks();
SDL_joysticks_quitting = SDL_TRUE;
while (SDL_joysticks) {
SDL_joysticks->ref_count = 1;
SDL_JoystickClose(SDL_joysticks);
}
for (i = SDL_arraysize(SDL_joystick_drivers) - 1; i >= 0; --i) {
SDL_joystick_drivers[i]->Quit();
}
if (SDL_joystick_players) {
SDL_free(SDL_joystick_players);
SDL_joystick_players = NULL;
SDL_joystick_player_count = 0;
}
#if !SDL_EVENTS_DISABLED
SDL_QuitSubSystem(SDL_INIT_EVENTS);
#endif
SDL_DelHintCallback(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS,
SDL_JoystickAllowBackgroundEventsChanged, NULL);
SDL_GameControllerQuitMappings();
SDL_joysticks_quitting = SDL_FALSE;
SDL_joysticks_initialized = SDL_FALSE;
SDL_UnlockJoysticks();
}
static SDL_bool
SDL_PrivateJoystickShouldIgnoreEvent()
{
if (SDL_joystick_allows_background_events) {
return SDL_FALSE;
}
if (SDL_HasWindows() && SDL_GetKeyboardFocus() == NULL) {
return SDL_TRUE;
}
return SDL_FALSE;
}
void SDL_PrivateJoystickAddTouchpad(SDL_Joystick *joystick, int nfingers)
{
int ntouchpads;
SDL_JoystickTouchpadInfo *touchpads;
CHECK_JOYSTICK_MAGIC(joystick, );
ntouchpads = joystick->ntouchpads + 1;
touchpads = (SDL_JoystickTouchpadInfo *)SDL_realloc(joystick->touchpads, (ntouchpads * sizeof(SDL_JoystickTouchpadInfo)));
if (touchpads) {
SDL_JoystickTouchpadInfo *touchpad = &touchpads[ntouchpads - 1];
SDL_JoystickTouchpadFingerInfo *fingers = (SDL_JoystickTouchpadFingerInfo *)SDL_calloc(nfingers, sizeof(SDL_JoystickTouchpadFingerInfo));
if (fingers) {
touchpad->nfingers = nfingers;
touchpad->fingers = fingers;
} else {
touchpad->nfingers = 0;
touchpad->fingers = NULL;
}
joystick->ntouchpads = ntouchpads;
joystick->touchpads = touchpads;
}
}
void SDL_PrivateJoystickAddSensor(SDL_Joystick *joystick, SDL_SensorType type, float rate)
{
int nsensors;
SDL_JoystickSensorInfo *sensors;
CHECK_JOYSTICK_MAGIC(joystick, );
nsensors = joystick->nsensors + 1;
sensors = (SDL_JoystickSensorInfo *)SDL_realloc(joystick->sensors, (nsensors * sizeof(SDL_JoystickSensorInfo)));
if (sensors) {
SDL_JoystickSensorInfo *sensor = &sensors[nsensors - 1];
SDL_zerop(sensor);
sensor->type = type;
sensor->rate = rate;
joystick->nsensors = nsensors;
joystick->sensors = sensors;
}
}
void SDL_PrivateJoystickAdded(SDL_JoystickID device_instance)
{
SDL_JoystickDriver *driver;
int driver_device_index;
int player_index = -1;
int device_index = SDL_JoystickGetDeviceIndexFromInstanceID(device_instance);
if (device_index < 0) {
return;
}
SDL_AssertJoysticksLocked();
if (SDL_JoysticksQuitting()) {
return;
}
if (SDL_GetDriverAndJoystickIndex(device_index, &driver, &driver_device_index)) {
player_index = driver->GetDevicePlayerIndex(driver_device_index);
}
if (player_index < 0 && SDL_IsGameController(device_index)) {
player_index = SDL_FindFreePlayerIndex();
}
if (player_index >= 0) {
SDL_SetJoystickIDForPlayerIndex(player_index, device_instance);
}
#if !SDL_EVENTS_DISABLED
{
SDL_Event event;
event.type = SDL_JOYDEVICEADDED;
if (SDL_GetEventState(event.type) == SDL_ENABLE) {
event.jdevice.which = device_index;
SDL_PushEvent(&event);
}
}
#endif
}
static void UpdateEventsForDeviceRemoval(int device_index, Uint32 type)
{
int i, num_events;
SDL_Event *events;
SDL_bool isstack;
num_events = SDL_PeepEvents(NULL, 0, SDL_PEEKEVENT, type, type);
if (num_events <= 0) {
return;
}
events = SDL_small_alloc(SDL_Event, num_events, &isstack);
if (!events) {
return;
}
num_events = SDL_PeepEvents(events, num_events, SDL_GETEVENT, type, type);
for (i = 0; i < num_events; ++i) {
Sint32 which = -1;
switch (type) {
case SDL_JOYDEVICEADDED:
which = events[i].jdevice.which;
break;
case SDL_CONTROLLERDEVICEADDED:
which = events[i].cdevice.which;
break;
default:
break;
}
if (which < device_index) {
} else if (which == device_index) {
SDL_memmove(&events[i], &events[i + 1], sizeof(*events) * (num_events - (i + 1)));
--num_events;
--i;
} else {
switch (type) {
case SDL_JOYDEVICEADDED:
--events[i].jdevice.which;
break;
case SDL_CONTROLLERDEVICEADDED:
--events[i].cdevice.which;
break;
default:
break;
}
}
}
SDL_PeepEvents(events, num_events, SDL_ADDEVENT, 0, 0);
SDL_small_free(events, isstack);
}
void
SDL_PrivateJoystickForceRecentering(SDL_Joystick *joystick)
{
int i, j;
CHECK_JOYSTICK_MAGIC(joystick, );
for (i = 0; i < joystick->naxes; i++) {
if (joystick->axes[i].has_initial_value) {
SDL_PrivateJoystickAxis(joystick, i, joystick->axes[i].zero);
}
}
for (i = 0; i < joystick->nbuttons; i++) {
SDL_PrivateJoystickButton(joystick, i, SDL_RELEASED);
}
for (i = 0; i < joystick->nhats; i++) {
SDL_PrivateJoystickHat(joystick, i, SDL_HAT_CENTERED);
}
for (i = 0; i < joystick->ntouchpads; i++) {
SDL_JoystickTouchpadInfo *touchpad = &joystick->touchpads[i];
for (j = 0; j < touchpad->nfingers; ++j) {
SDL_PrivateJoystickTouchpad(joystick, i, j, SDL_RELEASED, 0.0f, 0.0f, 0.0f);
}
}
}
void SDL_PrivateJoystickRemoved(SDL_JoystickID device_instance)
{
SDL_Joystick *joystick = NULL;
int player_index;
int device_index;
#if !SDL_EVENTS_DISABLED
SDL_Event event;
#endif
SDL_AssertJoysticksLocked();
device_index = 0;
for (joystick = SDL_joysticks; joystick; joystick = joystick->next) {
if (joystick->instance_id == device_instance) {
SDL_PrivateJoystickForceRecentering(joystick);
joystick->attached = SDL_FALSE;
break;
}
++device_index;
}
#if !SDL_EVENTS_DISABLED
SDL_zero(event);
event.type = SDL_JOYDEVICEREMOVED;
if (SDL_GetEventState(event.type) == SDL_ENABLE) {
event.jdevice.which = device_instance;
SDL_PushEvent(&event);
}
UpdateEventsForDeviceRemoval(device_index, SDL_JOYDEVICEADDED);
UpdateEventsForDeviceRemoval(device_index, SDL_CONTROLLERDEVICEADDED);
#endif
player_index = SDL_GetPlayerIndexForJoystickID(device_instance);
if (player_index >= 0) {
SDL_joystick_players[player_index] = -1;
}
}
int
SDL_PrivateJoystickAxis(SDL_Joystick *joystick, Uint8 axis, Sint16 value)
{
int posted;
SDL_JoystickAxisInfo *info;
SDL_AssertJoysticksLocked();
CHECK_JOYSTICK_MAGIC(joystick, 0);
if (axis >= joystick->naxes) {
return 0;
}
info = &joystick->axes[axis];
if (!info->has_initial_value ||
(!info->has_second_value && (info->initial_value <= -32767 || info->initial_value == 32767) && SDL_abs(value) < (SDL_JOYSTICK_AXIS_MAX / 4))) {
info->initial_value = value;
info->value = value;
info->zero = value;
info->has_initial_value = SDL_TRUE;
} else if (value == info->value && !info->sending_initial_value) {
return 0;
} else {
info->has_second_value = SDL_TRUE;
}
if (!info->sent_initial_value) {
const int MAX_ALLOWED_JITTER = SDL_JOYSTICK_AXIS_MAX / 80;
if (SDL_abs(value - info->value) <= MAX_ALLOWED_JITTER &&
!SDL_IsJoystickVirtual(joystick->guid)) {
return 0;
}
info->sent_initial_value = SDL_TRUE;
info->sending_initial_value = SDL_TRUE;
SDL_PrivateJoystickAxis(joystick, axis, info->initial_value);
info->sending_initial_value = SDL_FALSE;
}
if (SDL_PrivateJoystickShouldIgnoreEvent()) {
if (info->sending_initial_value ||
(value > info->zero && value >= info->value) ||
(value < info->zero && value <= info->value)) {
return 0;
}
}
info->value = value;
posted = 0;
#if !SDL_EVENTS_DISABLED
if (SDL_GetEventState(SDL_JOYAXISMOTION) == SDL_ENABLE) {
SDL_Event event;
event.type = SDL_JOYAXISMOTION;
event.jaxis.which = joystick->instance_id;
event.jaxis.axis = axis;
event.jaxis.value = value;
posted = SDL_PushEvent(&event) == 1;
}
#endif
return posted;
}
int
SDL_PrivateJoystickHat(SDL_Joystick *joystick, Uint8 hat, Uint8 value)
{
int posted;
SDL_AssertJoysticksLocked();
CHECK_JOYSTICK_MAGIC(joystick, 0);
if (hat >= joystick->nhats) {
return 0;
}
if (value == joystick->hats[hat]) {
return 0;
}
if (SDL_PrivateJoystickShouldIgnoreEvent()) {
if (value != SDL_HAT_CENTERED) {
return 0;
}
}
joystick->hats[hat] = value;
posted = 0;
#if !SDL_EVENTS_DISABLED
if (SDL_GetEventState(SDL_JOYHATMOTION) == SDL_ENABLE) {
SDL_Event event;
event.jhat.type = SDL_JOYHATMOTION;
event.jhat.which = joystick->instance_id;
event.jhat.hat = hat;
event.jhat.value = value;
posted = SDL_PushEvent(&event) == 1;
}
#endif
return posted;
}
int
SDL_PrivateJoystickBall(SDL_Joystick *joystick, Uint8 ball,
Sint16 xrel, Sint16 yrel)
{
int posted;
SDL_AssertJoysticksLocked();
CHECK_JOYSTICK_MAGIC(joystick, 0);
if (ball >= joystick->nballs) {
return 0;
}
if (SDL_PrivateJoystickShouldIgnoreEvent()) {
return 0;
}
joystick->balls[ball].dx += xrel;
joystick->balls[ball].dy += yrel;
posted = 0;
#if !SDL_EVENTS_DISABLED
if (SDL_GetEventState(SDL_JOYBALLMOTION) == SDL_ENABLE) {
SDL_Event event;
event.jball.type = SDL_JOYBALLMOTION;
event.jball.which = joystick->instance_id;
event.jball.ball = ball;
event.jball.xrel = xrel;
event.jball.yrel = yrel;
posted = SDL_PushEvent(&event) == 1;
}
#endif
return posted;
}
int
SDL_PrivateJoystickButton(SDL_Joystick *joystick, Uint8 button, Uint8 state)
{
int posted;
#if !SDL_EVENTS_DISABLED
SDL_Event event;
CHECK_JOYSTICK_MAGIC(joystick, 0);
switch (state) {
case SDL_PRESSED:
event.type = SDL_JOYBUTTONDOWN;
break;
case SDL_RELEASED:
event.type = SDL_JOYBUTTONUP;
break;
default:
return 0;
}
#endif
SDL_AssertJoysticksLocked();
if (button >= joystick->nbuttons) {
return 0;
}
if (state == joystick->buttons[button]) {
return 0;
}
if (SDL_PrivateJoystickShouldIgnoreEvent()) {
if (state == SDL_PRESSED) {
return 0;
}
}
joystick->buttons[button] = state;
posted = 0;
#if !SDL_EVENTS_DISABLED
if (SDL_GetEventState(event.type) == SDL_ENABLE) {
event.jbutton.which = joystick->instance_id;
event.jbutton.button = button;
event.jbutton.state = state;
posted = SDL_PushEvent(&event) == 1;
}
#endif
return posted;
}
void
SDL_JoystickUpdate(void)
{
int i;
Uint32 now;
SDL_Joystick *joystick;
if (!SDL_WasInit(SDL_INIT_JOYSTICK)) {
return;
}
SDL_LockJoysticks();
#ifdef SDL_JOYSTICK_HIDAPI
HIDAPI_UpdateDevices();
#endif
for (joystick = SDL_joysticks; joystick; joystick = joystick->next) {
if (joystick->attached) {
joystick->driver->Update(joystick);
if (joystick->delayed_guide_button) {
SDL_GameControllerHandleDelayedGuideButton(joystick);
}
}
now = SDL_GetTicks();
if (joystick->rumble_expiration &&
SDL_TICKS_PASSED(now, joystick->rumble_expiration)) {
SDL_JoystickRumble(joystick, 0, 0, 0);
joystick->rumble_resend = 0;
}
if (joystick->rumble_resend &&
SDL_TICKS_PASSED(now, joystick->rumble_resend)) {
joystick->driver->Rumble(joystick, joystick->low_frequency_rumble, joystick->high_frequency_rumble);
joystick->rumble_resend = now + SDL_RUMBLE_RESEND_MS;
if (joystick->rumble_resend == 0) {
joystick->rumble_resend = 1;
}
}
if (joystick->trigger_rumble_expiration &&
SDL_TICKS_PASSED(now, joystick->trigger_rumble_expiration)) {
SDL_JoystickRumbleTriggers(joystick, 0, 0, 0);
}
}
for (i = 0; i < SDL_arraysize(SDL_joystick_drivers); ++i) {
SDL_joystick_drivers[i]->Detect();
}
SDL_UnlockJoysticks();
}
int
SDL_JoystickEventState(int state)
{
#if SDL_EVENTS_DISABLED
return SDL_DISABLE;
#else
const Uint32 event_list[] = {
SDL_JOYAXISMOTION, SDL_JOYBALLMOTION, SDL_JOYHATMOTION,
SDL_JOYBUTTONDOWN, SDL_JOYBUTTONUP, SDL_JOYDEVICEADDED, SDL_JOYDEVICEREMOVED,
SDL_JOYBATTERYUPDATED
};
unsigned int i;
switch (state) {
case SDL_QUERY:
state = SDL_DISABLE;
for (i = 0; i < SDL_arraysize(event_list); ++i) {
state = SDL_EventState(event_list[i], SDL_QUERY);
if (state == SDL_ENABLE) {
break;
}
}
break;
default:
for (i = 0; i < SDL_arraysize(event_list); ++i) {
(void)SDL_EventState(event_list[i], state);
}
break;
}
return state;
#endif
}
void SDL_GetJoystickGUIDInfo(SDL_JoystickGUID guid, Uint16 *vendor, Uint16 *product, Uint16 *version, Uint16 *crc16)
{
Uint16 *guid16 = (Uint16 *)guid.data;
Uint16 bus = SDL_SwapLE16(guid16[0]);
if ((bus < ' ' || bus == SDL_HARDWARE_BUS_VIRTUAL) && guid16[3] == 0x0000 && guid16[5] == 0x0000) {
if (vendor) {
*vendor = SDL_SwapLE16(guid16[2]);
}
if (product) {
*product = SDL_SwapLE16(guid16[4]);
}
if (version) {
*version = SDL_SwapLE16(guid16[6]);
}
if (crc16) {
*crc16 = SDL_SwapLE16(guid16[1]);
}
} else if (bus < ' ') {
if (vendor) {
*vendor = 0;
}
if (product) {
*product = 0;
}
if (version) {
*version = 0;
}
if (crc16) {
*crc16 = SDL_SwapLE16(guid16[1]);
}
} else {
if (vendor) {
*vendor = 0;
}
if (product) {
*product = 0;
}
if (version) {
*version = 0;
}
if (crc16) {
*crc16 = 0;
}
}
}
static int
PrefixMatch(const char *a, const char *b)
{
int matchlen = 0;
while (*a && *b) {
if (SDL_tolower((unsigned char) *a++) == SDL_tolower((unsigned char) *b++)) {
++matchlen;
} else {
break;
}
}
return matchlen;
}
char *
SDL_CreateJoystickName(Uint16 vendor, Uint16 product, const char *vendor_name, const char *product_name)
{
static struct {
const char *prefix;
const char *replacement;
} replacements[] = {
{ "ASTRO Gaming", "ASTRO" },
{ "Bensussen Deutsch & Associates,Inc.(BDA)", "BDA" },
{ "NVIDIA Corporation ", "" },
{ "Performance Designed Products", "PDP" },
{ "HORI CO.,LTD.", "HORI" },
{ "HORI CO.,LTD", "HORI" },
{ "Mad Catz Inc.", "Mad Catz" },
{ "QANBA USA, LLC", "Qanba" },
{ "QANBA USA,LLC", "Qanba" },
{ "Unknown ", "" },
};
const char *custom_name;
char *name;
size_t i, len;
custom_name = GuessControllerName(vendor, product);
if (custom_name) {
return SDL_strdup(custom_name);
}
if (!vendor_name) {
vendor_name = "";
}
if (!product_name) {
product_name = "";
}
while (*vendor_name == ' ') {
++vendor_name;
}
while (*product_name == ' ') {
++product_name;
}
if (*vendor_name && *product_name) {
len = (SDL_strlen(vendor_name) + 1 + SDL_strlen(product_name) + 1);
name = (char *)SDL_malloc(len);
if (name) {
SDL_snprintf(name, len, "%s %s", vendor_name, product_name);
}
} else if (*product_name) {
name = SDL_strdup(product_name);
} else if (vendor || product) {
switch (SDL_GetJoystickGameControllerTypeFromVIDPID(vendor, product, NULL, SDL_TRUE)) {
case SDL_CONTROLLER_TYPE_XBOX360:
name = SDL_strdup("Xbox 360 Controller");
break;
case SDL_CONTROLLER_TYPE_XBOXONE:
name = SDL_strdup("Xbox One Controller");
break;
case SDL_CONTROLLER_TYPE_PS3:
name = SDL_strdup("PS3 Controller");
break;
case SDL_CONTROLLER_TYPE_PS4:
name = SDL_strdup("PS4 Controller");
break;
case SDL_CONTROLLER_TYPE_PS5:
name = SDL_strdup("PS5 Controller");
break;
case SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO:
name = SDL_strdup("Nintendo Switch Pro Controller");
break;
default:
len = (6 + 1 + 6 + 1);
name = (char *)SDL_malloc(len);
if (name) {
SDL_snprintf(name, len, "0x%.4x/0x%.4x", vendor, product);
}
break;
}
} else {
name = SDL_strdup("Controller");
}
if (!name) {
return NULL;
}
for (len = SDL_strlen(name); (len > 0 && name[len - 1] == ' '); --len) {
}
name[len] = '\0';
for (i = 0; i < (len - 1); ) {
if (name[i] == ' ' && name[i+1] == ' ') {
SDL_memmove(&name[i], &name[i+1], (len - i));
--len;
} else {
++i;
}
}
for (i = 0; i < SDL_arraysize(replacements); ++i) {
size_t prefixlen = SDL_strlen(replacements[i].prefix);
if (SDL_strncasecmp(name, replacements[i].prefix, prefixlen) == 0) {
size_t replacementlen = SDL_strlen(replacements[i].replacement);
if (replacementlen <= prefixlen) {
SDL_memcpy(name, replacements[i].replacement, replacementlen);
SDL_memmove(name+replacementlen, name+prefixlen, (len-prefixlen)+1);
len -= (prefixlen - replacementlen);
} else {
}
break;
}
}
for (i = 1; i < (len - 1); ++i) {
int matchlen = PrefixMatch(name, &name[i]);
while (matchlen > 0) {
if (name[matchlen] == ' ') {
SDL_memmove(name, name + matchlen + 1, len - matchlen);
break;
}
--matchlen;
}
if (matchlen > 0) {
break;
}
}
return name;
}
SDL_JoystickGUID
SDL_CreateJoystickGUID(Uint16 bus, Uint16 vendor, Uint16 product, Uint16 version, const char *name, Uint8 driver_signature, Uint8 driver_data)
{
SDL_JoystickGUID guid;
Uint16 *guid16 = (Uint16 *)guid.data;
SDL_zero(guid);
if (!name) {
name = "";
}
*guid16++ = SDL_SwapLE16(bus);
*guid16++ = SDL_SwapLE16(SDL_crc16(0, name, SDL_strlen(name)));
if (vendor && product) {
*guid16++ = SDL_SwapLE16(vendor);
*guid16++ = 0;
*guid16++ = SDL_SwapLE16(product);
*guid16++ = 0;
*guid16++ = SDL_SwapLE16(version);
guid.data[14] = driver_signature;
guid.data[15] = driver_data;
} else {
size_t available_space = sizeof(guid.data) - 4;
if (driver_signature) {
available_space -= 2;
guid.data[14] = driver_signature;
guid.data[15] = driver_data;
}
SDL_strlcpy((char*)guid16, name, available_space);
}
return guid;
}
SDL_JoystickGUID
SDL_CreateJoystickGUIDForName(const char *name)
{
return SDL_CreateJoystickGUID(SDL_HARDWARE_BUS_UNKNOWN, 0, 0, 0, name, 0, 0);
}
void SDL_SetJoystickGUIDVendor(SDL_JoystickGUID *guid, Uint16 vendor)
{
Uint16 *guid16 = (Uint16 *)guid->data;
guid16[2] = SDL_SwapLE16(vendor);
}
void SDL_SetJoystickGUIDProduct(SDL_JoystickGUID *guid, Uint16 product)
{
Uint16 *guid16 = (Uint16 *)guid->data;
guid16[4] = SDL_SwapLE16(product);
}
void SDL_SetJoystickGUIDVersion(SDL_JoystickGUID *guid, Uint16 version)
{
Uint16 *guid16 = (Uint16 *)guid->data;
guid16[6] = SDL_SwapLE16(version);
}
void
SDL_SetJoystickGUIDCRC(SDL_JoystickGUID *guid, Uint16 crc)
{
Uint16 *guid16 = (Uint16 *)guid->data;
guid16[1] = SDL_SwapLE16(crc);
}
SDL_GameControllerType
SDL_GetJoystickGameControllerTypeFromVIDPID(Uint16 vendor, Uint16 product, const char *name, SDL_bool forUI)
{
SDL_GameControllerType type = SDL_CONTROLLER_TYPE_UNKNOWN;
if (vendor == 0x0000 && product == 0x0000) {
if (name &&
(SDL_strcmp(name, "Lic Pro Controller") == 0 ||
SDL_strcmp(name, "Nintendo Wireless Gamepad") == 0 ||
SDL_strcmp(name, "Wireless Gamepad") == 0)) {
type = SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO;
}
} else if (vendor == 0x0001 && product == 0x0001) {
type = SDL_CONTROLLER_TYPE_UNKNOWN;
} else if (vendor == USB_VENDOR_MICROSOFT && product == USB_PRODUCT_XBOX_ONE_XINPUT_CONTROLLER) {
type = SDL_CONTROLLER_TYPE_XBOXONE;
} else if ((vendor == USB_VENDOR_AMAZON && product == USB_PRODUCT_AMAZON_LUNA_CONTROLLER) ||
(vendor == BLUETOOTH_VENDOR_AMAZON && product == BLUETOOTH_PRODUCT_LUNA_CONTROLLER)) {
type = SDL_CONTROLLER_TYPE_AMAZON_LUNA;
} else if (vendor == USB_VENDOR_GOOGLE && product == USB_PRODUCT_GOOGLE_STADIA_CONTROLLER) {
type = SDL_CONTROLLER_TYPE_GOOGLE_STADIA;
} else if (vendor == USB_VENDOR_NINTENDO && product == USB_PRODUCT_NINTENDO_SWITCH_JOYCON_LEFT) {
type = SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_LEFT;
} else if (vendor == USB_VENDOR_NINTENDO && product == USB_PRODUCT_NINTENDO_SWITCH_JOYCON_RIGHT) {
if (name && SDL_strstr(name, "NES Controller") != NULL) {
type = SDL_CONTROLLER_TYPE_UNKNOWN;
} else {
type = SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_RIGHT;
}
} else if (vendor == USB_VENDOR_NINTENDO && product == USB_PRODUCT_NINTENDO_SWITCH_JOYCON_GRIP) {
if (name && SDL_strstr(name, "(L)") != NULL) {
type = SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_LEFT;
} else {
type = SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_RIGHT;
}
} else if (vendor == USB_VENDOR_NINTENDO && product == USB_PRODUCT_NINTENDO_SWITCH_JOYCON_PAIR) {
type = SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_JOYCON_PAIR;
} else if (vendor == USB_VENDOR_NVIDIA &&
(product == USB_PRODUCT_NVIDIA_SHIELD_CONTROLLER_V103 ||
product == USB_PRODUCT_NVIDIA_SHIELD_CONTROLLER_V104)) {
type = SDL_CONTROLLER_TYPE_NVIDIA_SHIELD;
} else {
switch (GuessControllerType(vendor, product)) {
case k_eControllerType_XBox360Controller:
type = SDL_CONTROLLER_TYPE_XBOX360;
break;
case k_eControllerType_XBoxOneController:
type = SDL_CONTROLLER_TYPE_XBOXONE;
break;
case k_eControllerType_PS3Controller:
type = SDL_CONTROLLER_TYPE_PS3;
break;
case k_eControllerType_PS4Controller:
type = SDL_CONTROLLER_TYPE_PS4;
break;
case k_eControllerType_PS5Controller:
type = SDL_CONTROLLER_TYPE_PS5;
break;
case k_eControllerType_XInputPS4Controller:
if (forUI) {
type = SDL_CONTROLLER_TYPE_PS4;
} else {
type = SDL_CONTROLLER_TYPE_UNKNOWN;
}
break;
case k_eControllerType_SwitchProController:
case k_eControllerType_SwitchInputOnlyController:
type = SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO;
break;
case k_eControllerType_XInputSwitchController:
if (forUI) {
type = SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO;
} else {
type = SDL_CONTROLLER_TYPE_UNKNOWN;
}
break;
default:
break;
}
}
return type;
}
SDL_GameControllerType
SDL_GetJoystickGameControllerTypeFromGUID(SDL_JoystickGUID guid, const char *name)
{
SDL_GameControllerType type;
Uint16 vendor, product;
SDL_GetJoystickGUIDInfo(guid, &vendor, &product, NULL, NULL);
type = SDL_GetJoystickGameControllerTypeFromVIDPID(vendor, product, name, SDL_TRUE);
if (type == SDL_CONTROLLER_TYPE_UNKNOWN) {
if (SDL_IsJoystickXInput(guid)) {
return SDL_CONTROLLER_TYPE_XBOXONE;
}
if (SDL_IsJoystickVirtual(guid)) {
return SDL_CONTROLLER_TYPE_VIRTUAL;
}
#ifdef SDL_JOYSTICK_HIDAPI
if (SDL_IsJoystickHIDAPI(guid)) {
return HIDAPI_GetGameControllerTypeFromGUID(guid);
}
#endif
}
return type;
}
SDL_bool
SDL_IsJoystickXboxOne(Uint16 vendor_id, Uint16 product_id)
{
EControllerType eType = GuessControllerType(vendor_id, product_id);
return (eType == k_eControllerType_XBoxOneController);
}
SDL_bool
SDL_IsJoystickXboxOneElite(Uint16 vendor_id, Uint16 product_id)
{
if (vendor_id == USB_VENDOR_MICROSOFT) {
if (product_id == USB_PRODUCT_XBOX_ONE_ELITE_SERIES_1 ||
product_id == USB_PRODUCT_XBOX_ONE_ELITE_SERIES_2 ||
product_id == USB_PRODUCT_XBOX_ONE_ELITE_SERIES_2_BLUETOOTH) {
return SDL_TRUE;
}
}
return SDL_FALSE;
}
SDL_bool
SDL_IsJoystickXboxSeriesX(Uint16 vendor_id, Uint16 product_id)
{
if (vendor_id == USB_VENDOR_MICROSOFT) {
if (product_id == USB_PRODUCT_XBOX_SERIES_X ||
product_id == USB_PRODUCT_XBOX_SERIES_X_BLE) {
return SDL_TRUE;
}
}
if (vendor_id == USB_VENDOR_PDP) {
if (product_id == USB_PRODUCT_XBOX_SERIES_X_VICTRIX_GAMBIT ||
product_id == USB_PRODUCT_XBOX_SERIES_X_PDP_BLUE ||
product_id == USB_PRODUCT_XBOX_SERIES_X_PDP_AFTERGLOW) {
return SDL_TRUE;
}
}
if (vendor_id == USB_VENDOR_POWERA_ALT) {
if ((product_id >= 0x2001 && product_id <= 0x201a) ||
product_id == USB_PRODUCT_XBOX_SERIES_X_POWERA_FUSION_PRO2 ||
product_id == USB_PRODUCT_XBOX_SERIES_X_POWERA_SPECTRA) {
return SDL_TRUE;
}
}
if (vendor_id == USB_VENDOR_HORI) {
if (product_id == USB_PRODUCT_HORI_FIGHTING_COMMANDER_OCTA_SERIES_X) {
return SDL_TRUE;
}
}
if (vendor_id == USB_VENDOR_8BITDO) {
if (product_id == USB_PRODUCT_8BITDO_XBOX_CONTROLLER) {
return SDL_TRUE;
}
}
return SDL_FALSE;
}
SDL_bool
SDL_IsJoystickBluetoothXboxOne(Uint16 vendor_id, Uint16 product_id)
{
if (vendor_id == USB_VENDOR_MICROSOFT) {
if (product_id == USB_PRODUCT_XBOX_ONE_ADAPTIVE_BLUETOOTH ||
product_id == USB_PRODUCT_XBOX_ONE_ADAPTIVE_BLE ||
product_id == USB_PRODUCT_XBOX_ONE_S_REV1_BLUETOOTH ||
product_id == USB_PRODUCT_XBOX_ONE_S_REV2_BLUETOOTH ||
product_id == USB_PRODUCT_XBOX_ONE_S_REV2_BLE ||
product_id == USB_PRODUCT_XBOX_ONE_ELITE_SERIES_2_BLUETOOTH ||
product_id == USB_PRODUCT_XBOX_ONE_ELITE_SERIES_2_BLE ||
product_id == USB_PRODUCT_XBOX_SERIES_X_BLE) {
return SDL_TRUE;
}
}
return SDL_FALSE;
}
SDL_bool
SDL_IsJoystickPS4(Uint16 vendor_id, Uint16 product_id)
{
EControllerType eType = GuessControllerType(vendor_id, product_id);
return (eType == k_eControllerType_PS4Controller);
}
SDL_bool
SDL_IsJoystickPS5(Uint16 vendor_id, Uint16 product_id)
{
EControllerType eType = GuessControllerType(vendor_id, product_id);
return (eType == k_eControllerType_PS5Controller);
}
SDL_bool
SDL_IsJoystickNintendoSwitchPro(Uint16 vendor_id, Uint16 product_id)
{
EControllerType eType = GuessControllerType(vendor_id, product_id);
return (eType == k_eControllerType_SwitchProController ||
eType == k_eControllerType_SwitchInputOnlyController);
}
SDL_bool
SDL_IsJoystickNintendoSwitchProInputOnly(Uint16 vendor_id, Uint16 product_id)
{
EControllerType eType = GuessControllerType(vendor_id, product_id);
return (eType == k_eControllerType_SwitchInputOnlyController);
}
SDL_bool
SDL_IsJoystickNintendoSwitchJoyCon(Uint16 vendor_id, Uint16 product_id)
{
EControllerType eType = GuessControllerType(vendor_id, product_id);
return (eType == k_eControllerType_SwitchJoyConLeft ||
eType == k_eControllerType_SwitchJoyConRight);
}
SDL_bool
SDL_IsJoystickNintendoSwitchJoyConLeft(Uint16 vendor_id, Uint16 product_id)
{
EControllerType eType = GuessControllerType(vendor_id, product_id);
return (eType == k_eControllerType_SwitchJoyConLeft);
}
SDL_bool
SDL_IsJoystickNintendoSwitchJoyConRight(Uint16 vendor_id, Uint16 product_id)
{
EControllerType eType = GuessControllerType(vendor_id, product_id);
return (eType == k_eControllerType_SwitchJoyConRight);
}
SDL_bool
SDL_IsJoystickNintendoSwitchJoyConGrip(Uint16 vendor_id, Uint16 product_id)
{
return (vendor_id == USB_VENDOR_NINTENDO && product_id == USB_PRODUCT_NINTENDO_SWITCH_JOYCON_GRIP);
}
SDL_bool
SDL_IsJoystickNintendoSwitchJoyConPair(Uint16 vendor_id, Uint16 product_id)
{
return (vendor_id == USB_VENDOR_NINTENDO && product_id == USB_PRODUCT_NINTENDO_SWITCH_JOYCON_PAIR);
}
SDL_bool
SDL_IsJoystickSteamController(Uint16 vendor_id, Uint16 product_id)
{
EControllerType eType = GuessControllerType(vendor_id, product_id);
return (eType == k_eControllerType_SteamController ||
eType == k_eControllerType_SteamControllerV2);
}
SDL_bool
SDL_IsJoystickXInput(SDL_JoystickGUID guid)
{
return (guid.data[14] == 'x') ? SDL_TRUE : SDL_FALSE;
}
SDL_bool
SDL_IsJoystickWGI(SDL_JoystickGUID guid)
{
return (guid.data[14] == 'w') ? SDL_TRUE : SDL_FALSE;
}
SDL_bool
SDL_IsJoystickHIDAPI(SDL_JoystickGUID guid)
{
return (guid.data[14] == 'h') ? SDL_TRUE : SDL_FALSE;
}
SDL_bool
SDL_IsJoystickRAWINPUT(SDL_JoystickGUID guid)
{
return (guid.data[14] == 'r') ? SDL_TRUE : SDL_FALSE;
}
SDL_bool
SDL_IsJoystickVirtual(SDL_JoystickGUID guid)
{
return (guid.data[14] == 'v') ? SDL_TRUE : SDL_FALSE;
}
static SDL_bool SDL_IsJoystickProductWheel(Uint32 vidpid)
{
static Uint32 wheel_joysticks[] = {
MAKE_VIDPID(0x046d, 0xc294),
MAKE_VIDPID(0x046d, 0xc295),
MAKE_VIDPID(0x046d, 0xc298),
MAKE_VIDPID(0x046d, 0xc299),
MAKE_VIDPID(0x046d, 0xc29a),
MAKE_VIDPID(0x046d, 0xc29b),
MAKE_VIDPID(0x046d, 0xc24f),
MAKE_VIDPID(0x046d, 0xc260),
MAKE_VIDPID(0x046d, 0xc261),
MAKE_VIDPID(0x046d, 0xc262),
MAKE_VIDPID(0x046d, 0xc26e),
MAKE_VIDPID(0x046d, 0xca03),
MAKE_VIDPID(0x044f, 0xb65d),
MAKE_VIDPID(0x044f, 0xb66d),
MAKE_VIDPID(0x044f, 0xb677),
MAKE_VIDPID(0x044f, 0xb696),
MAKE_VIDPID(0x044f, 0xb66e),
MAKE_VIDPID(0x044f, 0xb66f),
MAKE_VIDPID(0x044f, 0xb66d),
MAKE_VIDPID(0x044f, 0xb65e),
MAKE_VIDPID(0x044f, 0xb664),
MAKE_VIDPID(0x044f, 0xb669),
};
int i;
for (i = 0; i < SDL_arraysize(wheel_joysticks); ++i) {
if (vidpid == wheel_joysticks[i]) {
return SDL_TRUE;
}
}
return SDL_FALSE;
}
static SDL_bool SDL_IsJoystickProductArcadeStick(Uint32 vidpid)
{
static Uint32 arcadestick_joysticks[] = {
MAKE_VIDPID(0x0079, 0x181a),
MAKE_VIDPID(0x0079, 0x181b),
MAKE_VIDPID(0x0c12, 0x0ef6),
MAKE_VIDPID(0x0e6f, 0x0109),
MAKE_VIDPID(0x0f0d, 0x0016),
MAKE_VIDPID(0x0f0d, 0x001b),
MAKE_VIDPID(0x0f0d, 0x0063),
MAKE_VIDPID(0x0f0d, 0x006a),
MAKE_VIDPID(0x0f0d, 0x0078),
MAKE_VIDPID(0x0f0d, 0x008a),
MAKE_VIDPID(0x0f0d, 0x008c),
MAKE_VIDPID(0x0f0d, 0x00aa),
MAKE_VIDPID(0x0f0d, 0x00ed),
MAKE_VIDPID(0x0f0d, 0x011c),
MAKE_VIDPID(0x0f0d, 0x011e),
MAKE_VIDPID(0x0f0d, 0x0184),
MAKE_VIDPID(0x1532, 0x0a00),
MAKE_VIDPID(0x1bad, 0xf03d),
MAKE_VIDPID(0x1bad, 0xf502),
MAKE_VIDPID(0x1bad, 0xf504),
MAKE_VIDPID(0x1bad, 0xf506),
MAKE_VIDPID(0x20d6, 0xa715),
MAKE_VIDPID(0x24c6, 0x5000),
MAKE_VIDPID(0x24c6, 0x5501),
MAKE_VIDPID(0x24c6, 0x550e),
MAKE_VIDPID(0x2c22, 0x2300),
MAKE_VIDPID(0x2c22, 0x2302),
MAKE_VIDPID(0x2c22, 0x2303),
MAKE_VIDPID(0x2c22, 0x2500),
MAKE_VIDPID(0x2c22, 0x2502),
MAKE_VIDPID(0x2c22, 0x2503),
};
int i;
for (i = 0; i < SDL_arraysize(arcadestick_joysticks); ++i) {
if (vidpid == arcadestick_joysticks[i]) {
return SDL_TRUE;
}
}
return SDL_FALSE;
}
static SDL_bool SDL_IsJoystickProductFlightStick(Uint32 vidpid)
{
static Uint32 flightstick_joysticks[] = {
MAKE_VIDPID(0x044f, 0x0402),
MAKE_VIDPID(0x0738, 0x2221),
MAKE_VIDPID(0x044f, 0xb10a),
MAKE_VIDPID(0x046d, 0xc215),
MAKE_VIDPID(0x231d, 0x0126),
MAKE_VIDPID(0x231d, 0x0127),
};
int i;
for (i = 0; i < SDL_arraysize(flightstick_joysticks); ++i) {
if (vidpid == flightstick_joysticks[i]) {
return SDL_TRUE;
}
}
return SDL_FALSE;
}
static SDL_bool SDL_IsJoystickProductThrottle(Uint32 vidpid)
{
static Uint32 throttle_joysticks[] = {
MAKE_VIDPID(0x044f, 0x0404),
MAKE_VIDPID(0x0738, 0xa221),
};
int i;
for (i = 0; i < SDL_arraysize(throttle_joysticks); ++i) {
if (vidpid == throttle_joysticks[i]) {
return SDL_TRUE;
}
}
return SDL_FALSE;
}
static SDL_JoystickType SDL_GetJoystickGUIDType(SDL_JoystickGUID guid)
{
Uint16 vendor;
Uint16 product;
Uint32 vidpid;
if (SDL_IsJoystickXInput(guid)) {
switch (guid.data[15]) {
case 0x01:
return SDL_JOYSTICK_TYPE_GAMECONTROLLER;
case 0x02:
return SDL_JOYSTICK_TYPE_WHEEL;
case 0x03:
return SDL_JOYSTICK_TYPE_ARCADE_STICK;
case 0x04:
return SDL_JOYSTICK_TYPE_FLIGHT_STICK;
case 0x05:
return SDL_JOYSTICK_TYPE_DANCE_PAD;
case 0x06:
case 0x07:
case 0x0B:
return SDL_JOYSTICK_TYPE_GUITAR;
case 0x08:
return SDL_JOYSTICK_TYPE_DRUM_KIT;
case 0x13:
return SDL_JOYSTICK_TYPE_ARCADE_PAD;
default:
return SDL_JOYSTICK_TYPE_UNKNOWN;
}
}
if (SDL_IsJoystickWGI(guid)) {
return (SDL_JoystickType)guid.data[15];
}
if (SDL_IsJoystickVirtual(guid)) {
return (SDL_JoystickType)guid.data[15];
}
SDL_GetJoystickGUIDInfo(guid, &vendor, &product, NULL, NULL);
vidpid = MAKE_VIDPID(vendor, product);
if (SDL_IsJoystickProductWheel(vidpid)) {
return SDL_JOYSTICK_TYPE_WHEEL;
}
if (SDL_IsJoystickProductArcadeStick(vidpid)) {
return SDL_JOYSTICK_TYPE_ARCADE_STICK;
}
if (SDL_IsJoystickProductFlightStick(vidpid)) {
return SDL_JOYSTICK_TYPE_FLIGHT_STICK;
}
if (SDL_IsJoystickProductThrottle(vidpid)) {
return SDL_JOYSTICK_TYPE_THROTTLE;
}
#ifdef SDL_JOYSTICK_HIDAPI
if (SDL_IsJoystickHIDAPI(guid)) {
return HIDAPI_GetJoystickTypeFromGUID(guid);
}
#endif
if (GuessControllerType(vendor, product) != k_eControllerType_UnknownNonSteamController) {
return SDL_JOYSTICK_TYPE_GAMECONTROLLER;
}
return SDL_JOYSTICK_TYPE_UNKNOWN;
}
SDL_bool SDL_ShouldIgnoreJoystick(const char *name, SDL_JoystickGUID guid)
{
static Uint32 joystick_blacklist[] = {
MAKE_VIDPID(0x045e, 0x009d),
MAKE_VIDPID(0x045e, 0x00b0),
MAKE_VIDPID(0x045e, 0x00b4),
MAKE_VIDPID(0x045e, 0x0730),
MAKE_VIDPID(0x045e, 0x0745),
MAKE_VIDPID(0x045e, 0x0748),
MAKE_VIDPID(0x045e, 0x0750),
MAKE_VIDPID(0x045e, 0x0768),
MAKE_VIDPID(0x045e, 0x0773),
MAKE_VIDPID(0x045e, 0x07a5),
MAKE_VIDPID(0x045e, 0x07b2),
MAKE_VIDPID(0x045e, 0x0800),
MAKE_VIDPID(0x046d, 0xc30a),
MAKE_VIDPID(0x04d9, 0xa0df),
MAKE_VIDPID(0x056a, 0x0010),
MAKE_VIDPID(0x056a, 0x0011),
MAKE_VIDPID(0x056a, 0x0012),
MAKE_VIDPID(0x056a, 0x0013),
MAKE_VIDPID(0x056a, 0x0014),
MAKE_VIDPID(0x056a, 0x0015),
MAKE_VIDPID(0x056a, 0x0016),
MAKE_VIDPID(0x056a, 0x0017),
MAKE_VIDPID(0x056a, 0x0018),
MAKE_VIDPID(0x056a, 0x0019),
MAKE_VIDPID(0x056a, 0x00d1),
MAKE_VIDPID(0x056a, 0x030e),
MAKE_VIDPID(0x09da, 0x054f),
MAKE_VIDPID(0x09da, 0x1410),
MAKE_VIDPID(0x09da, 0x3043),
MAKE_VIDPID(0x09da, 0x31b5),
MAKE_VIDPID(0x09da, 0x3997),
MAKE_VIDPID(0x09da, 0x3f8b),
MAKE_VIDPID(0x09da, 0x51f4),
MAKE_VIDPID(0x09da, 0x5589),
MAKE_VIDPID(0x09da, 0x7b22),
MAKE_VIDPID(0x09da, 0x7f2d),
MAKE_VIDPID(0x09da, 0x8090),
MAKE_VIDPID(0x09da, 0x9033),
MAKE_VIDPID(0x09da, 0x9066),
MAKE_VIDPID(0x09da, 0x9090),
MAKE_VIDPID(0x09da, 0x90c0),
MAKE_VIDPID(0x09da, 0xf012),
MAKE_VIDPID(0x09da, 0xf32a),
MAKE_VIDPID(0x09da, 0xf613),
MAKE_VIDPID(0x09da, 0xf624),
MAKE_VIDPID(0x1b1c, 0x1b3c),
MAKE_VIDPID(0x1d57, 0xad03),
MAKE_VIDPID(0x1e7d, 0x2e4a),
MAKE_VIDPID(0x20a0, 0x422d),
MAKE_VIDPID(0x2516, 0x001f),
MAKE_VIDPID(0x2516, 0x0028),
MAKE_VIDPID(0x04d9, 0x8008),
MAKE_VIDPID(0x04d9, 0x8009),
MAKE_VIDPID(0x04d9, 0xa292),
MAKE_VIDPID(0x04d9, 0xa293),
MAKE_VIDPID(0x1532, 0x0266),
MAKE_VIDPID(0x1532, 0x0282),
MAKE_VIDPID(0x26ce, 0x01a2),
MAKE_VIDPID(0x20d6, 0x0002),
};
static Uint32 rog_chakram_list[] = {
MAKE_VIDPID(0x0b05, 0x1958),
MAKE_VIDPID(0x0b05, 0x18e3),
MAKE_VIDPID(0x0b05, 0x18e5),
MAKE_VIDPID(0x0b05, 0x1a18),
MAKE_VIDPID(0x0b05, 0x1a1a),
MAKE_VIDPID(0x0b05, 0x1a1c),
};
unsigned int i;
Uint32 id;
Uint16 vendor;
Uint16 product;
SDL_GetJoystickGUIDInfo(guid, &vendor, &product, NULL, NULL);
id = MAKE_VIDPID(vendor, product);
for (i = 0; i < SDL_arraysize(joystick_blacklist); ++i) {
if (id == joystick_blacklist[i]) {
return SDL_TRUE;
}
}
if (!SDL_GetHintBoolean(SDL_HINT_JOYSTICK_ROG_CHAKRAM, SDL_FALSE)) {
for (i = 0; i < SDL_arraysize(rog_chakram_list); ++i) {
if (id == rog_chakram_list[i]) {
return SDL_TRUE;
}
}
}
if (SDL_ShouldIgnoreGameController(name, guid)) {
return SDL_TRUE;
}
return SDL_FALSE;
}
SDL_JoystickGUID SDL_JoystickGetDeviceGUID(int device_index)
{
SDL_JoystickDriver *driver;
SDL_JoystickGUID guid;
SDL_LockJoysticks();
if (SDL_GetDriverAndJoystickIndex(device_index, &driver, &device_index)) {
guid = driver->GetDeviceGUID(device_index);
} else {
SDL_zero(guid);
}
SDL_UnlockJoysticks();
return guid;
}
Uint16 SDL_JoystickGetDeviceVendor(int device_index)
{
Uint16 vendor;
SDL_JoystickGUID guid = SDL_JoystickGetDeviceGUID(device_index);
SDL_GetJoystickGUIDInfo(guid, &vendor, NULL, NULL, NULL);
return vendor;
}
Uint16 SDL_JoystickGetDeviceProduct(int device_index)
{
Uint16 product;
SDL_JoystickGUID guid = SDL_JoystickGetDeviceGUID(device_index);
SDL_GetJoystickGUIDInfo(guid, NULL, &product, NULL, NULL);
return product;
}
Uint16 SDL_JoystickGetDeviceProductVersion(int device_index)
{
Uint16 version;
SDL_JoystickGUID guid = SDL_JoystickGetDeviceGUID(device_index);
SDL_GetJoystickGUIDInfo(guid, NULL, NULL, &version, NULL);
return version;
}
SDL_JoystickType SDL_JoystickGetDeviceType(int device_index)
{
SDL_JoystickType type;
SDL_JoystickGUID guid = SDL_JoystickGetDeviceGUID(device_index);
type = SDL_GetJoystickGUIDType(guid);
if (type == SDL_JOYSTICK_TYPE_UNKNOWN) {
if (SDL_IsGameController(device_index)) {
type = SDL_JOYSTICK_TYPE_GAMECONTROLLER;
}
}
return type;
}
SDL_JoystickID SDL_JoystickGetDeviceInstanceID(int device_index)
{
SDL_JoystickDriver *driver;
SDL_JoystickID instance_id = -1;
SDL_LockJoysticks();
if (SDL_GetDriverAndJoystickIndex(device_index, &driver, &device_index)) {
instance_id = driver->GetDeviceInstanceID(device_index);
}
SDL_UnlockJoysticks();
return instance_id;
}
int SDL_JoystickGetDeviceIndexFromInstanceID(SDL_JoystickID instance_id)
{
int i, num_joysticks, device_index = -1;
SDL_LockJoysticks();
num_joysticks = SDL_NumJoysticks();
for (i = 0; i < num_joysticks; ++i) {
if (SDL_JoystickGetDeviceInstanceID(i) == instance_id) {
device_index = i;
break;
}
}
SDL_UnlockJoysticks();
return device_index;
}
SDL_JoystickGUID SDL_JoystickGetGUID(SDL_Joystick *joystick)
{
static SDL_JoystickGUID emptyGUID;
CHECK_JOYSTICK_MAGIC(joystick, emptyGUID);
return joystick->guid;
}
Uint16 SDL_JoystickGetVendor(SDL_Joystick *joystick)
{
Uint16 vendor;
SDL_JoystickGUID guid = SDL_JoystickGetGUID(joystick);
SDL_GetJoystickGUIDInfo(guid, &vendor, NULL, NULL, NULL);
return vendor;
}
Uint16 SDL_JoystickGetProduct(SDL_Joystick *joystick)
{
Uint16 product;
SDL_JoystickGUID guid = SDL_JoystickGetGUID(joystick);
SDL_GetJoystickGUIDInfo(guid, NULL, &product, NULL, NULL);
return product;
}
Uint16 SDL_JoystickGetProductVersion(SDL_Joystick *joystick)
{
Uint16 version;
SDL_JoystickGUID guid = SDL_JoystickGetGUID(joystick);
SDL_GetJoystickGUIDInfo(guid, NULL, NULL, &version, NULL);
return version;
}
Uint16 SDL_JoystickGetFirmwareVersion(SDL_Joystick *joystick)
{
CHECK_JOYSTICK_MAGIC(joystick, 0);
return joystick->firmware_version;
}
const char *SDL_JoystickGetSerial(SDL_Joystick *joystick)
{
CHECK_JOYSTICK_MAGIC(joystick, NULL);
return joystick->serial;
}
SDL_JoystickType SDL_JoystickGetType(SDL_Joystick *joystick)
{
SDL_JoystickType type;
SDL_JoystickGUID guid = SDL_JoystickGetGUID(joystick);
type = SDL_GetJoystickGUIDType(guid);
if (type == SDL_JOYSTICK_TYPE_UNKNOWN) {
if (joystick && joystick->is_game_controller) {
type = SDL_JOYSTICK_TYPE_GAMECONTROLLER;
}
}
return type;
}
void SDL_JoystickGetGUIDString(SDL_JoystickGUID guid, char *pszGUID, int cbGUID)
{
SDL_GUIDToString(guid, pszGUID, cbGUID);
}
SDL_JoystickGUID SDL_JoystickGetGUIDFromString(const char *pchGUID)
{
return SDL_GUIDFromString(pchGUID);
}
void SDL_PrivateJoystickBatteryLevel(SDL_Joystick *joystick, SDL_JoystickPowerLevel ePowerLevel)
{
CHECK_JOYSTICK_MAGIC(joystick, );
SDL_assert(joystick->ref_count);
if (ePowerLevel != joystick->epowerlevel) {
#if !SDL_EVENTS_DISABLED
if (SDL_GetEventState(SDL_JOYBATTERYUPDATED) == SDL_ENABLE) {
SDL_Event event;
event.type = SDL_JOYBATTERYUPDATED;
event.jbattery.which = joystick->instance_id;
event.jbattery.level = ePowerLevel;
SDL_PushEvent(&event);
}
#endif
joystick->epowerlevel = ePowerLevel;
}
}
SDL_JoystickPowerLevel SDL_JoystickCurrentPowerLevel(SDL_Joystick *joystick)
{
CHECK_JOYSTICK_MAGIC(joystick, SDL_JOYSTICK_POWER_UNKNOWN);
return joystick->epowerlevel;
}
int SDL_PrivateJoystickTouchpad(SDL_Joystick *joystick, int touchpad, int finger, Uint8 state, float x, float y, float pressure)
{
SDL_JoystickTouchpadInfo *touchpad_info;
SDL_JoystickTouchpadFingerInfo *finger_info;
int posted;
Uint32 event_type;
CHECK_JOYSTICK_MAGIC(joystick, 0);
if (touchpad < 0 || touchpad >= joystick->ntouchpads) {
return 0;
}
touchpad_info = &joystick->touchpads[touchpad];
if (finger < 0 || finger >= touchpad_info->nfingers) {
return 0;
}
finger_info = &touchpad_info->fingers[finger];
if (!state) {
if (x == 0.0f && y == 0.0f) {
x = finger_info->x;
y = finger_info->y;
}
pressure = 0.0f;
}
if (x < 0.0f) {
x = 0.0f;
} else if (x > 1.0f) {
x = 1.0f;
}
if (y < 0.0f) {
y = 0.0f;
} else if (y > 1.0f) {
y = 1.0f;
}
if (pressure < 0.0f) {
pressure = 0.0f;
} else if (pressure > 1.0f) {
pressure = 1.0f;
}
if (state == finger_info->state) {
if (!state ||
(x == finger_info->x && y == finger_info->y && pressure == finger_info->pressure)) {
return 0;
}
}
if (state == finger_info->state) {
event_type = SDL_CONTROLLERTOUCHPADMOTION;
} else if (state) {
event_type = SDL_CONTROLLERTOUCHPADDOWN;
} else {
event_type = SDL_CONTROLLERTOUCHPADUP;
}
if (SDL_PrivateJoystickShouldIgnoreEvent()) {
if (event_type != SDL_CONTROLLERTOUCHPADUP) {
return 0;
}
}
finger_info->state = state;
finger_info->x = x;
finger_info->y = y;
finger_info->pressure = pressure;
posted = 0;
#if !SDL_EVENTS_DISABLED
if (SDL_GetEventState(event_type) == SDL_ENABLE) {
SDL_Event event;
event.type = event_type;
event.ctouchpad.which = joystick->instance_id;
event.ctouchpad.touchpad = touchpad;
event.ctouchpad.finger = finger;
event.ctouchpad.x = x;
event.ctouchpad.y = y;
event.ctouchpad.pressure = pressure;
posted = SDL_PushEvent(&event) == 1;
}
#endif
return posted;
}
int SDL_PrivateJoystickSensor(SDL_Joystick *joystick, SDL_SensorType type, Uint64 timestamp_us, const float *data, int num_values)
{
int i;
int posted = 0;
CHECK_JOYSTICK_MAGIC(joystick, 0);
if (SDL_PrivateJoystickShouldIgnoreEvent()) {
return 0;
}
for (i = 0; i < joystick->nsensors; ++i) {
SDL_JoystickSensorInfo *sensor = &joystick->sensors[i];
if (sensor->type == type) {
if (sensor->enabled) {
num_values = SDL_min(num_values, SDL_arraysize(sensor->data));
SDL_memcpy(sensor->data, data, num_values*sizeof(*data));
sensor->timestamp_us = timestamp_us;
#if !SDL_EVENTS_DISABLED
if (SDL_GetEventState(SDL_CONTROLLERSENSORUPDATE) == SDL_ENABLE) {
SDL_Event event;
event.type = SDL_CONTROLLERSENSORUPDATE;
event.csensor.which = joystick->instance_id;
event.csensor.sensor = type;
num_values = SDL_min(num_values, SDL_arraysize(event.csensor.data));
SDL_memset(event.csensor.data, 0, sizeof(event.csensor.data));
SDL_memcpy(event.csensor.data, data, num_values*sizeof(*data));
event.csensor.timestamp_us = timestamp_us;
posted = SDL_PushEvent(&event) == 1;
}
#endif
}
break;
}
}
return posted;
}