| /* |
| Simple DirectMedia Layer |
| Copyright (C) 1997-2021 Sam Lantinga <slouken@libsdl.org> |
| |
| This software is provided 'as-is', without any express or implied |
| warranty. In no event will the authors be held liable for any damages |
| arising from the use of this software. |
| |
| Permission is granted to anyone to use this software for any purpose, |
| including commercial applications, and to alter it and redistribute it |
| freely, subject to the following restrictions: |
| |
| 1. The origin of this software must not be misrepresented; you must not |
| claim that you wrote the original software. If you use this software |
| in a product, an acknowledgment in the product documentation would be |
| appreciated but is not required. |
| 2. Altered source versions must be plainly marked as such, and must not be |
| misrepresented as being the original software. |
| 3. This notice may not be removed or altered from any source distribution. |
| */ |
| #include "../SDL_internal.h" |
| |
| /* This is the joystick API for Simple DirectMedia Layer */ |
| |
| #include "SDL.h" |
| #include "SDL_atomic.h" |
| #include "SDL_events.h" |
| #include "SDL_sysjoystick.h" |
| #include "SDL_hints.h" |
| |
| #if !SDL_EVENTS_DISABLED |
| #include "../events/SDL_events_c.h" |
| #endif |
| #include "../video/SDL_sysvideo.h" |
| #include "hidapi/SDL_hidapijoystick_c.h" |
| |
| /* This is included in only one place because it has a large static list of controllers */ |
| #include "controller_type.h" |
| |
| #ifdef __WIN32__ |
| /* Needed for checking for input remapping programs */ |
| #include "../core/windows/SDL_windows.h" |
| |
| #undef UNICODE /* We want ASCII functions */ |
| #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 /* Before WINDOWS_ driver, as WINDOWS wants to check if this driver is handling things */ |
| &SDL_HIDAPI_JoystickDriver, |
| #endif |
| #ifdef SDL_JOYSTICK_RAWINPUT /* Before WINDOWS_ driver, as WINDOWS wants to check if this driver is handling things */ |
| &SDL_RAWINPUT_JoystickDriver, |
| #endif |
| #if defined(SDL_JOYSTICK_WGI) |
| &SDL_WGI_JoystickDriver, |
| #endif |
| #if defined(SDL_JOYSTICK_DINPUT) || defined(SDL_JOYSTICK_XINPUT) |
| &SDL_WINDOWS_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 /* !!! FIXME: "USBHID" is a generic name, and doubly-confusing with HIDAPI next to it. This is the *BSD interface, rename this. */ |
| &SDL_BSD_JoystickDriver, |
| #endif |
| #ifdef SDL_JOYSTICK_OS2 |
| &SDL_OS2_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 |
| #if defined(SDL_JOYSTICK_DUMMY) || defined(SDL_JOYSTICK_DISABLED) |
| &SDL_DUMMY_JoystickDriver |
| #endif |
| }; |
| static SDL_bool SDL_joystick_allows_background_events = SDL_FALSE; |
| static SDL_Joystick *SDL_joysticks = NULL; |
| static SDL_bool SDL_updating_joystick = SDL_FALSE; |
| static SDL_mutex *SDL_joystick_lock = NULL; /* This needs to support recursive locks */ |
| static SDL_atomic_t SDL_next_joystick_instance_id; |
| static int SDL_joystick_player_count = 0; |
| static SDL_JoystickID *SDL_joystick_players = NULL; |
| |
| void |
| SDL_LockJoysticks(void) |
| { |
| if (SDL_joystick_lock) { |
| SDL_LockMutex(SDL_joystick_lock); |
| } |
| } |
| |
| void |
| SDL_UnlockJoysticks(void) |
| { |
| if (SDL_joystick_lock) { |
| SDL_UnlockMutex(SDL_joystick_lock); |
| } |
| } |
| |
| static int |
| SDL_FindFreePlayerIndex() |
| { |
| int player_index; |
| |
| 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; |
| |
| 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) |
| { |
| 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; |
| |
| 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 (SDL_joystick_players[player_index] == instance_id) { |
| /* Joystick is already assigned the requested player index */ |
| return SDL_TRUE; |
| } |
| |
| /* Clear the old player index */ |
| 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; |
| } |
| |
| /* Update the driver with the new index */ |
| device_index = SDL_JoystickGetDeviceIndexFromInstanceID(instance_id); |
| if (SDL_GetDriverAndJoystickIndex(device_index, &driver, &device_index)) { |
| driver->SetDevicePlayerIndex(device_index, player_index); |
| } |
| |
| /* Move any existing joystick to another slot */ |
| 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 (hint && *hint == '1') { |
| SDL_joystick_allows_background_events = SDL_TRUE; |
| } else { |
| SDL_joystick_allows_background_events = SDL_FALSE; |
| } |
| } |
| |
| int |
| SDL_JoystickInit(void) |
| { |
| int i, status; |
| |
| SDL_GameControllerInitMappings(); |
| |
| /* Create the joystick list lock */ |
| if (!SDL_joystick_lock) { |
| SDL_joystick_lock = SDL_CreateMutex(); |
| } |
| |
| /* See if we should allow joystick events while in the background */ |
| SDL_AddHintCallback(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, |
| SDL_JoystickAllowBackgroundEventsChanged, NULL); |
| |
| #if !SDL_EVENTS_DISABLED |
| if (SDL_InitSubSystem(SDL_INIT_EVENTS) < 0) { |
| return -1; |
| } |
| #endif /* !SDL_EVENTS_DISABLED */ |
| |
| status = -1; |
| for (i = 0; i < SDL_arraysize(SDL_joystick_drivers); ++i) { |
| if (SDL_joystick_drivers[i]->Init() >= 0) { |
| status = 0; |
| } |
| } |
| return status; |
| } |
| |
| /* |
| * Count the number of joysticks attached to the system |
| */ |
| 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; |
| } |
| |
| /* |
| * Return the next available joystick instance ID |
| * This may be called by drivers from multiple threads, unprotected by any locks |
| */ |
| SDL_JoystickID SDL_GetNextJoystickInstanceID() |
| { |
| return SDL_AtomicIncRef(&SDL_next_joystick_instance_id); |
| } |
| |
| /* |
| * Get the driver and device index for an API device index |
| * This should be called while the joystick lock is held, to prevent another thread from updating the list |
| */ |
| SDL_bool |
| SDL_GetDriverAndJoystickIndex(int device_index, SDL_JoystickDriver **driver, int *driver_index) |
| { |
| int i, num_joysticks, total_joysticks = 0; |
| |
| 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; |
| } |
| |
| /* |
| * Get the implementation dependent name of a joystick |
| */ |
| 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(); |
| |
| /* FIXME: Really we should reference count this name so it doesn't go away after unlock */ |
| return name; |
| } |
| |
| /* |
| * Get the player index of a joystick, or -1 if it's not available |
| */ |
| int |
| SDL_JoystickGetDevicePlayerIndex(int device_index) |
| { |
| int player_index; |
| |
| SDL_LockJoysticks(); |
| player_index = SDL_GetPlayerIndexForJoystickID(SDL_JoystickGetDeviceInstanceID(device_index)); |
| SDL_UnlockJoysticks(); |
| |
| return player_index; |
| } |
| |
| /* |
| * Return true if this joystick is known to have all axes centered at zero |
| * This isn't generally needed unless the joystick never generates an initial axis value near zero, |
| * e.g. it's emulating axes with digital buttons |
| */ |
| static SDL_bool |
| SDL_JoystickAxesCenteredAtZero(SDL_Joystick *joystick) |
| { |
| #ifdef __WINRT__ |
| return SDL_TRUE; |
| #else |
| static Uint32 zero_centered_joysticks[] = { |
| MAKE_VIDPID(0x0e8f, 0x3013), /* HuiJia SNES USB adapter */ |
| MAKE_VIDPID(0x05a0, 0x3232), /* 8Bitdo Zero Gamepad */ |
| }; |
| |
| int i; |
| Uint32 id = MAKE_VIDPID(SDL_JoystickGetVendor(joystick), |
| SDL_JoystickGetProduct(joystick)); |
| |
| /*printf("JOYSTICK '%s' VID/PID 0x%.4x/0x%.4x AXES: %d\n", joystick->name, vendor, product, joystick->naxes);*/ |
| |
| if (joystick->naxes == 2) { |
| /* Assume D-pad or thumbstick style axes are centered at 0 */ |
| 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 /* __WINRT__ */ |
| } |
| |
| /* |
| * Open a joystick for use - the index passed as an argument refers to |
| * the N'th joystick on the system. This index is the value which will |
| * identify this joystick in future joystick events. |
| * |
| * This function returns a joystick identifier, or NULL if an error occurred. |
| */ |
| SDL_Joystick * |
| SDL_JoystickOpen(int device_index) |
| { |
| SDL_JoystickDriver *driver; |
| SDL_JoystickID instance_id; |
| SDL_Joystick *joystick; |
| SDL_Joystick *joysticklist; |
| const char *joystickname = NULL; |
| |
| SDL_LockJoysticks(); |
| |
| if (!SDL_GetDriverAndJoystickIndex(device_index, &driver, &device_index)) { |
| SDL_UnlockJoysticks(); |
| return NULL; |
| } |
| |
| joysticklist = SDL_joysticks; |
| /* If the joystick is already open, return it |
| * it is important that we have a single joystick * for each instance id |
| */ |
| 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; |
| } |
| |
| /* Create and initialize the joystick */ |
| joystick = (SDL_Joystick *) SDL_calloc(sizeof(*joystick), 1); |
| if (joystick == NULL) { |
| SDL_OutOfMemory(); |
| SDL_UnlockJoysticks(); |
| return NULL; |
| } |
| 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; |
| } |
| |
| 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 this joystick is known to have all zero centered axes, skip the auto-centering code */ |
| 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); |
| |
| /* Add joystick to list */ |
| ++joystick->ref_count; |
| /* Link the joystick in the list */ |
| joystick->next = SDL_joysticks; |
| SDL_joysticks = joystick; |
| |
| SDL_UnlockJoysticks(); |
| |
| driver->Update(joystick); |
| |
| return joystick; |
| } |
| |
| int |
| SDL_JoystickAttachVirtual(SDL_JoystickType type, |
| int naxes, int nbuttons, int nhats) |
| { |
| #if SDL_JOYSTICK_VIRTUAL |
| return SDL_JoystickAttachVirtualInner(type, naxes, nbuttons, nhats); |
| #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) |
| { |
| #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) |
| { |
| #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) |
| { |
| #if SDL_JOYSTICK_VIRTUAL |
| return SDL_JoystickSetVirtualHatInner(joystick, hat, value); |
| #else |
| return SDL_SetError("SDL not built with virtual-joystick support"); |
| #endif |
| } |
| |
| /* |
| * Checks to make sure the joystick is valid. |
| */ |
| SDL_bool |
| SDL_PrivateJoystickValid(SDL_Joystick *joystick) |
| { |
| SDL_bool valid; |
| |
| if (joystick == NULL) { |
| SDL_SetError("Joystick hasn't been opened yet"); |
| valid = SDL_FALSE; |
| } else { |
| valid = SDL_TRUE; |
| } |
| |
| return valid; |
| } |
| |
| 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; |
| } |
| |
| /* |
| * Get the number of multi-dimensional axis controls on a joystick |
| */ |
| int |
| SDL_JoystickNumAxes(SDL_Joystick *joystick) |
| { |
| if (!SDL_PrivateJoystickValid(joystick)) { |
| return -1; |
| } |
| return joystick->naxes; |
| } |
| |
| /* |
| * Get the number of hats on a joystick |
| */ |
| int |
| SDL_JoystickNumHats(SDL_Joystick *joystick) |
| { |
| if (!SDL_PrivateJoystickValid(joystick)) { |
| return -1; |
| } |
| return joystick->nhats; |
| } |
| |
| /* |
| * Get the number of trackballs on a joystick |
| */ |
| int |
| SDL_JoystickNumBalls(SDL_Joystick *joystick) |
| { |
| if (!SDL_PrivateJoystickValid(joystick)) { |
| return -1; |
| } |
| return joystick->nballs; |
| } |
| |
| /* |
| * Get the number of buttons on a joystick |
| */ |
| int |
| SDL_JoystickNumButtons(SDL_Joystick *joystick) |
| { |
| if (!SDL_PrivateJoystickValid(joystick)) { |
| return -1; |
| } |
| return joystick->nbuttons; |
| } |
| |
| /* |
| * Get the current state of an axis control on a joystick |
| */ |
| Sint16 |
| SDL_JoystickGetAxis(SDL_Joystick *joystick, int axis) |
| { |
| Sint16 state; |
| |
| if (!SDL_PrivateJoystickValid(joystick)) { |
| return 0; |
| } |
| if (axis < joystick->naxes) { |
| state = joystick->axes[axis].value; |
| } else { |
| SDL_SetError("Joystick only has %d axes", joystick->naxes); |
| state = 0; |
| } |
| return state; |
| } |
| |
| /* |
| * Get the initial state of an axis control on a joystick |
| */ |
| SDL_bool |
| SDL_JoystickGetAxisInitialState(SDL_Joystick *joystick, int axis, Sint16 *state) |
| { |
| if (!SDL_PrivateJoystickValid(joystick)) { |
| return 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; |
| } |
| |
| /* |
| * Get the current state of a hat on a joystick |
| */ |
| Uint8 |
| SDL_JoystickGetHat(SDL_Joystick *joystick, int hat) |
| { |
| Uint8 state; |
| |
| if (!SDL_PrivateJoystickValid(joystick)) { |
| return 0; |
| } |
| if (hat < joystick->nhats) { |
| state = joystick->hats[hat]; |
| } else { |
| SDL_SetError("Joystick only has %d hats", joystick->nhats); |
| state = 0; |
| } |
| return state; |
| } |
| |
| /* |
| * Get the ball axis change since the last poll |
| */ |
| int |
| SDL_JoystickGetBall(SDL_Joystick *joystick, int ball, int *dx, int *dy) |
| { |
| int retval; |
| |
| if (!SDL_PrivateJoystickValid(joystick)) { |
| return -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; |
| } |
| |
| /* |
| * Get the current state of a button on a joystick |
| */ |
| Uint8 |
| SDL_JoystickGetButton(SDL_Joystick *joystick, int button) |
| { |
| Uint8 state; |
| |
| if (!SDL_PrivateJoystickValid(joystick)) { |
| return 0; |
| } |
| if (button < joystick->nbuttons) { |
| state = joystick->buttons[button]; |
| } else { |
| SDL_SetError("Joystick only has %d buttons", joystick->nbuttons); |
| state = 0; |
| } |
| return state; |
| } |
| |
| /* |
| * Return if the joystick in question is currently attached to the system, |
| * \return SDL_FALSE if not plugged in, SDL_TRUE if still present. |
| */ |
| SDL_bool |
| SDL_JoystickGetAttached(SDL_Joystick *joystick) |
| { |
| if (!joystick) { |
| return SDL_FALSE; |
| } |
| |
| return joystick->attached; |
| } |
| |
| /* |
| * Get the instance id for this opened joystick |
| */ |
| SDL_JoystickID |
| SDL_JoystickInstanceID(SDL_Joystick *joystick) |
| { |
| if (!SDL_PrivateJoystickValid(joystick)) { |
| return -1; |
| } |
| |
| return joystick->instance_id; |
| } |
| |
| /* |
| * Return the SDL_Joystick associated with an 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; |
| } |
| |
| /** |
| * Return the SDL_Joystick associated with a player index. |
| */ |
| 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; |
| } |
| |
| /* |
| * Get the friendly name of this joystick |
| */ |
| const char * |
| SDL_JoystickName(SDL_Joystick *joystick) |
| { |
| if (!SDL_PrivateJoystickValid(joystick)) { |
| return NULL; |
| } |
| |
| return joystick->name; |
| } |
| |
| /** |
| * Get the player index of an opened joystick, or -1 if it's not available |
| */ |
| int |
| SDL_JoystickGetPlayerIndex(SDL_Joystick *joystick) |
| { |
| int player_index; |
| |
| if (!SDL_PrivateJoystickValid(joystick)) { |
| return -1; |
| } |
| |
| SDL_LockJoysticks(); |
| player_index = SDL_GetPlayerIndexForJoystickID(joystick->instance_id); |
| SDL_UnlockJoysticks(); |
| |
| return player_index; |
| } |
| |
| /** |
| * Set the player index of an opened joystick |
| */ |
| void |
| SDL_JoystickSetPlayerIndex(SDL_Joystick *joystick, int player_index) |
| { |
| if (!SDL_PrivateJoystickValid(joystick)) { |
| return; |
| } |
| |
| 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; |
| |
| if (!SDL_PrivateJoystickValid(joystick)) { |
| return -1; |
| } |
| |
| SDL_LockJoysticks(); |
| if (low_frequency_rumble == joystick->low_frequency_rumble && |
| high_frequency_rumble == joystick->high_frequency_rumble) { |
| /* Just update the expiration */ |
| result = 0; |
| } else { |
| result = joystick->driver->Rumble(joystick, low_frequency_rumble, high_frequency_rumble); |
| } |
| |
| /* Save the rumble value regardless of success, so we don't spam the driver */ |
| 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; |
| } |
| SDL_UnlockJoysticks(); |
| |
| return result; |
| } |
| |
| int |
| SDL_JoystickRumbleTriggers(SDL_Joystick *joystick, Uint16 left_rumble, Uint16 right_rumble, Uint32 duration_ms) |
| { |
| int result; |
| |
| if (!SDL_PrivateJoystickValid(joystick)) { |
| return -1; |
| } |
| |
| SDL_LockJoysticks(); |
| if (left_rumble == joystick->left_trigger_rumble && right_rumble == joystick->right_trigger_rumble) { |
| /* Just update the expiration */ |
| result = 0; |
| } else { |
| result = joystick->driver->RumbleTriggers(joystick, left_rumble, right_rumble); |
| } |
| |
| /* Save the rumble value regardless of success, so we don't spam the driver */ |
| 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; |
| |
| if (!SDL_PrivateJoystickValid(joystick)) { |
| return 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; |
| |
| if (!SDL_PrivateJoystickValid(joystick)) { |
| return 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; |
| |
| if (!SDL_PrivateJoystickValid(joystick)) { |
| return 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; |
| |
| if (!SDL_PrivateJoystickValid(joystick)) { |
| return -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 { |
| /* Avoid spamming the driver */ |
| result = 0; |
| } |
| |
| /* Save the LED value regardless of success, so we don't spam the driver */ |
| 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; |
| |
| if (!SDL_PrivateJoystickValid(joystick)) { |
| return -1; |
| } |
| |
| SDL_LockJoysticks(); |
| |
| result = joystick->driver->SendEffect(joystick, data, size); |
| |
| SDL_UnlockJoysticks(); |
| |
| return result; |
| } |
| |
| /* |
| * Close a joystick previously opened with SDL_JoystickOpen() |
| */ |
| void |
| SDL_JoystickClose(SDL_Joystick *joystick) |
| { |
| SDL_Joystick *joysticklist; |
| SDL_Joystick *joysticklistprev; |
| int i; |
| |
| if (!SDL_PrivateJoystickValid(joystick)) { |
| return; |
| } |
| |
| SDL_LockJoysticks(); |
| |
| /* First decrement ref count */ |
| if (--joystick->ref_count > 0) { |
| SDL_UnlockJoysticks(); |
| return; |
| } |
| |
| if (SDL_updating_joystick) { |
| 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; |
| |
| joysticklist = SDL_joysticks; |
| joysticklistprev = NULL; |
| while (joysticklist) { |
| if (joystick == joysticklist) { |
| if (joysticklistprev) { |
| /* unlink this entry */ |
| joysticklistprev->next = joysticklist->next; |
| } else { |
| SDL_joysticks = joystick->next; |
| } |
| break; |
| } |
| joysticklistprev = joysticklist; |
| joysticklist = joysticklist->next; |
| } |
| |
| SDL_free(joystick->name); |
| SDL_free(joystick->serial); |
| |
| /* Free the data associated with this joystick */ |
| 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; |
| |
| /* Make sure we're not getting called in the middle of updating joysticks */ |
| SDL_LockJoysticks(); |
| while (SDL_updating_joystick) { |
| SDL_UnlockJoysticks(); |
| SDL_Delay(1); |
| SDL_LockJoysticks(); |
| } |
| |
| /* Stop the event polling */ |
| while (SDL_joysticks) { |
| SDL_joysticks->ref_count = 1; |
| SDL_JoystickClose(SDL_joysticks); |
| } |
| |
| /* Quit drivers in reverse order to avoid breaking dependencies between drivers */ |
| 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; |
| } |
| SDL_UnlockJoysticks(); |
| |
| #if !SDL_EVENTS_DISABLED |
| SDL_QuitSubSystem(SDL_INIT_EVENTS); |
| #endif |
| |
| SDL_DelHintCallback(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, |
| SDL_JoystickAllowBackgroundEventsChanged, NULL); |
| |
| if (SDL_joystick_lock) { |
| SDL_mutex *mutex = SDL_joystick_lock; |
| SDL_joystick_lock = NULL; |
| SDL_DestroyMutex(mutex); |
| } |
| |
| SDL_GameControllerQuitMappings(); |
| } |
| |
| |
| static SDL_bool |
| SDL_PrivateJoystickShouldIgnoreEvent() |
| { |
| if (SDL_joystick_allows_background_events) { |
| return SDL_FALSE; |
| } |
| |
| if (SDL_HasWindows() && SDL_GetKeyboardFocus() == NULL) { |
| /* We have windows but we don't have focus, ignore the event. */ |
| return SDL_TRUE; |
| } |
| return SDL_FALSE; |
| } |
| |
| /* These are global for SDL_sysjoystick.c and SDL_events.c */ |
| |
| void SDL_PrivateJoystickAddTouchpad(SDL_Joystick *joystick, int nfingers) |
| { |
| int ntouchpads = joystick->ntouchpads + 1; |
| SDL_JoystickTouchpadInfo *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 { |
| /* Out of memory, this touchpad won't be active */ |
| 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 = joystick->nsensors + 1; |
| SDL_JoystickSensorInfo *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_LockJoysticks(); |
| 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); |
| } |
| SDL_UnlockJoysticks(); |
| |
| #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 /* !SDL_EVENTS_DISABLED */ |
| } |
| |
| /* |
| * If there is an existing add event in the queue, it needs to be modified |
| * to have the right value for which, because the number of controllers in |
| * the system is now one less. |
| */ |
| static void UpdateEventsForDeviceRemoval(int device_index) |
| { |
| int i, num_events; |
| SDL_Event *events; |
| SDL_bool isstack; |
| |
| num_events = SDL_PeepEvents(NULL, 0, SDL_PEEKEVENT, SDL_JOYDEVICEADDED, SDL_JOYDEVICEADDED); |
| 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, SDL_JOYDEVICEADDED, SDL_JOYDEVICEADDED); |
| for (i = 0; i < num_events; ++i) { |
| if (events[i].cdevice.which < device_index) { |
| /* No change for index values lower than the removed device */ |
| } |
| else if (events[i].cdevice.which == device_index) { |
| /* Drop this event entirely */ |
| SDL_memmove(&events[i], &events[i + 1], sizeof(*events) * (num_events - (i + 1))); |
| --num_events; |
| --i; |
| } |
| else { |
| /* Fix up the device index if greater than the removed device */ |
| --events[i].cdevice.which; |
| } |
| } |
| SDL_PeepEvents(events, num_events, SDL_ADDEVENT, 0, 0); |
| |
| SDL_small_free(events, isstack); |
| } |
| |
| static void |
| SDL_PrivateJoystickForceRecentering(SDL_Joystick *joystick) |
| { |
| int i, j; |
| |
| /* Tell the app that everything is centered/unpressed... */ |
| 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 |
| |
| /* Find this joystick... */ |
| 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); |
| #endif /* !SDL_EVENTS_DISABLED */ |
| |
| SDL_LockJoysticks(); |
| player_index = SDL_GetPlayerIndexForJoystickID(device_instance); |
| if (player_index >= 0) { |
| SDL_joystick_players[player_index] = -1; |
| } |
| SDL_UnlockJoysticks(); |
| } |
| |
| int |
| SDL_PrivateJoystickAxis(SDL_Joystick *joystick, Uint8 axis, Sint16 value) |
| { |
| int posted; |
| SDL_JoystickAxisInfo *info; |
| |
| /* Make sure we're not getting garbage or duplicate events */ |
| 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) { |
| /* Make sure we don't send motion until there's real activity on this axis */ |
| const int MAX_ALLOWED_JITTER = SDL_JOYSTICK_AXIS_MAX / 80; /* ShanWan PS3 controller needed 96 */ |
| if (SDL_abs(value - info->value) <= MAX_ALLOWED_JITTER) { |
| 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; |
| } |
| |
| /* We ignore events if we don't have keyboard focus, except for centering |
| * events. |
| */ |
| if (SDL_PrivateJoystickShouldIgnoreEvent()) { |
| if (info->sending_initial_value || |
| (value > info->zero && value >= info->value) || |
| (value < info->zero && value <= info->value)) { |
| return 0; |
| } |
| } |
| |
| /* Update internal joystick state */ |
| info->value = value; |
| |
| /* Post the event, if desired */ |
| 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 /* !SDL_EVENTS_DISABLED */ |
| return posted; |
| } |
| |
| int |
| SDL_PrivateJoystickHat(SDL_Joystick *joystick, Uint8 hat, Uint8 value) |
| { |
| int posted; |
| |
| /* Make sure we're not getting garbage or duplicate events */ |
| if (hat >= joystick->nhats) { |
| return 0; |
| } |
| if (value == joystick->hats[hat]) { |
| return 0; |
| } |
| |
| /* We ignore events if we don't have keyboard focus, except for centering |
| * events. |
| */ |
| if (SDL_PrivateJoystickShouldIgnoreEvent()) { |
| if (value != SDL_HAT_CENTERED) { |
| return 0; |
| } |
| } |
| |
| /* Update internal joystick state */ |
| joystick->hats[hat] = value; |
| |
| /* Post the event, if desired */ |
| 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 /* !SDL_EVENTS_DISABLED */ |
| return posted; |
| } |
| |
| int |
| SDL_PrivateJoystickBall(SDL_Joystick *joystick, Uint8 ball, |
| Sint16 xrel, Sint16 yrel) |
| { |
| int posted; |
| |
| /* Make sure we're not getting garbage events */ |
| if (ball >= joystick->nballs) { |
| return 0; |
| } |
| |
| /* We ignore events if we don't have keyboard focus. */ |
| if (SDL_PrivateJoystickShouldIgnoreEvent()) { |
| return 0; |
| } |
| |
| /* Update internal mouse state */ |
| joystick->balls[ball].dx += xrel; |
| joystick->balls[ball].dy += yrel; |
| |
| /* Post the event, if desired */ |
| 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 /* !SDL_EVENTS_DISABLED */ |
| return posted; |
| } |
| |
| int |
| SDL_PrivateJoystickButton(SDL_Joystick *joystick, Uint8 button, Uint8 state) |
| { |
| int posted; |
| #if !SDL_EVENTS_DISABLED |
| SDL_Event event; |
| |
| switch (state) { |
| case SDL_PRESSED: |
| event.type = SDL_JOYBUTTONDOWN; |
| break; |
| case SDL_RELEASED: |
| event.type = SDL_JOYBUTTONUP; |
| break; |
| default: |
| /* Invalid state -- bail */ |
| return 0; |
| } |
| #endif /* !SDL_EVENTS_DISABLED */ |
| |
| /* Make sure we're not getting garbage or duplicate events */ |
| if (button >= joystick->nbuttons) { |
| return 0; |
| } |
| if (state == joystick->buttons[button]) { |
| return 0; |
| } |
| |
| /* We ignore events if we don't have keyboard focus, except for button |
| * release. */ |
| if (SDL_PrivateJoystickShouldIgnoreEvent()) { |
| if (state == SDL_PRESSED) { |
| return 0; |
| } |
| } |
| |
| /* Update internal joystick state */ |
| joystick->buttons[button] = state; |
| |
| /* Post the event, if desired */ |
| 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 /* !SDL_EVENTS_DISABLED */ |
| return posted; |
| } |
| |
| void |
| SDL_JoystickUpdate(void) |
| { |
| int i; |
| SDL_Joystick *joystick, *next; |
| |
| if (!SDL_WasInit(SDL_INIT_JOYSTICK)) { |
| return; |
| } |
| |
| SDL_LockJoysticks(); |
| |
| if (SDL_updating_joystick) { |
| /* The joysticks are already being updated */ |
| SDL_UnlockJoysticks(); |
| return; |
| } |
| |
| SDL_updating_joystick = SDL_TRUE; |
| |
| /* Make sure the list is unlocked while dispatching events to prevent application deadlocks */ |
| SDL_UnlockJoysticks(); |
| |
| #ifdef SDL_JOYSTICK_HIDAPI |
| /* Special function for HIDAPI devices, as a single device can provide multiple SDL_Joysticks */ |
| HIDAPI_UpdateDevices(); |
| #endif /* SDL_JOYSTICK_HIDAPI */ |
| |
| for (joystick = SDL_joysticks; joystick; joystick = joystick->next) { |
| if (joystick->attached) { |
| /* This should always be true, but seeing a crash in the wild...? */ |
| if (joystick->driver) { |
| joystick->driver->Update(joystick); |
| } |
| |
| if (joystick->delayed_guide_button) { |
| SDL_GameControllerHandleDelayedGuideButton(joystick); |
| } |
| } |
| |
| if (joystick->rumble_expiration) { |
| SDL_LockJoysticks(); |
| /* Double check now that the lock is held */ |
| if (joystick->rumble_expiration && |
| SDL_TICKS_PASSED(SDL_GetTicks(), joystick->rumble_expiration)) { |
| SDL_JoystickRumble(joystick, 0, 0, 0); |
| } |
| SDL_UnlockJoysticks(); |
| } |
| |
| if (joystick->trigger_rumble_expiration) { |
| SDL_LockJoysticks(); |
| /* Double check now that the lock is held */ |
| if (joystick->trigger_rumble_expiration && |
| SDL_TICKS_PASSED(SDL_GetTicks(), joystick->trigger_rumble_expiration)) { |
| SDL_JoystickRumbleTriggers(joystick, 0, 0, 0); |
| } |
| SDL_UnlockJoysticks(); |
| } |
| } |
| |
| SDL_LockJoysticks(); |
| |
| SDL_updating_joystick = SDL_FALSE; |
| |
| /* If any joysticks were closed while updating, free them here */ |
| for (joystick = SDL_joysticks; joystick; joystick = next) { |
| next = joystick->next; |
| if (joystick->ref_count <= 0) { |
| SDL_JoystickClose(joystick); |
| } |
| } |
| |
| /* this needs to happen AFTER walking the joystick list above, so that any |
| dangling hardware data from removed devices can be free'd |
| */ |
| 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 |
| }; |
| 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) { |
| SDL_EventState(event_list[i], state); |
| } |
| break; |
| } |
| return state; |
| #endif /* SDL_EVENTS_DISABLED */ |
| } |
| |
| void SDL_GetJoystickGUIDInfo(SDL_JoystickGUID guid, Uint16 *vendor, Uint16 *product, Uint16 *version) |
| { |
| Uint16 *guid16 = (Uint16 *)guid.data; |
| |
| /* If the GUID fits the form of BUS 0000 VENDOR 0000 PRODUCT 0000, return the data */ |
| if (/* guid16[0] is device bus type */ |
| guid16[1] == 0x0000 && |
| /* guid16[2] is vendor ID */ |
| guid16[3] == 0x0000 && |
| /* guid16[4] is product ID */ |
| guid16[5] == 0x0000 |
| /* guid16[6] is product version */ |
| ) { |
| if (vendor) { |
| *vendor = guid16[2]; |
| } |
| if (product) { |
| *product = guid16[4]; |
| } |
| if (version) { |
| *version = guid16[6]; |
| } |
| } else { |
| if (vendor) { |
| *vendor = 0; |
| } |
| if (product) { |
| *product = 0; |
| } |
| if (version) { |
| *version = 0; |
| } |
| } |
| } |
| |
| static int |
| PrefixMatch(const char *a, const char *b) |
| { |
| int matchlen = 0; |
| while (*a && *b) { |
| if (SDL_tolower(*a++) == SDL_tolower(*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[] = { |
| { "NVIDIA Corporation ", "" }, |
| { "Performance Designed Products", "PDP" }, |
| { "HORI CO.,LTD.", "HORI" }, |
| { "HORI CO.,LTD", "HORI" }, |
| { "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) { |
| return NULL; |
| } |
| SDL_snprintf(name, len, "%s %s", vendor_name, product_name); |
| } else if (*product_name) { |
| name = SDL_strdup(product_name); |
| } else if (vendor || product) { |
| len = (6 + 1 + 6 + 1); |
| name = (char *)SDL_malloc(len); |
| if (!name) { |
| return NULL; |
| } |
| SDL_snprintf(name, len, "0x%.4x/0x%.4x", vendor, product); |
| } else { |
| name = SDL_strdup("Controller"); |
| } |
| |
| /* Trim trailing whitespace */ |
| for (len = SDL_strlen(name); (len > 0 && name[len - 1] == ' '); --len) { |
| /* continue */ |
| } |
| name[len] = '\0'; |
| |
| /* Compress duplicate spaces */ |
| for (i = 0; i < (len - 1); ) { |
| if (name[i] == ' ' && name[i+1] == ' ') { |
| SDL_memmove(&name[i], &name[i+1], (len - i)); |
| --len; |
| } else { |
| ++i; |
| } |
| } |
| |
| /* Perform any manufacturer replacements */ |
| 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 { |
| /* FIXME: Need to handle the expand case by reallocating the string */ |
| } |
| break; |
| } |
| } |
| |
| /* Remove duplicate manufacturer or product in the name */ |
| for (i = 1; i < (len - 1); ++i) { |
| int matchlen = PrefixMatch(name, &name[i]); |
| if (matchlen > 0 && name[matchlen-1] == ' ') { |
| SDL_memmove(name, name+matchlen, len-matchlen+1); |
| break; |
| } else if (matchlen > 0 && name[matchlen] == ' ') { |
| SDL_memmove(name, name+matchlen+1, len-matchlen); |
| break; |
| } |
| } |
| |
| return name; |
| } |
| |
| SDL_GameControllerType |
| SDL_GetJoystickGameControllerTypeFromVIDPID(Uint16 vendor, Uint16 product) |
| { |
| return SDL_GetJoystickGameControllerType(NULL, vendor, product, -1, 0, 0, 0); |
| } |
| |
| SDL_GameControllerType |
| SDL_GetJoystickGameControllerTypeFromGUID(SDL_JoystickGUID guid, const char *name) |
| { |
| SDL_GameControllerType type; |
| Uint16 vendor, product; |
| |
| SDL_GetJoystickGUIDInfo(guid, &vendor, &product, NULL); |
| type = SDL_GetJoystickGameControllerType(name, vendor, product, -1, 0, 0, 0); |
| if (type == SDL_CONTROLLER_TYPE_UNKNOWN) { |
| if (SDL_IsJoystickXInput(guid)) { |
| /* This is probably an Xbox One controller */ |
| return SDL_CONTROLLER_TYPE_XBOXONE; |
| } |
| } |
| return type; |
| } |
| |
| SDL_GameControllerType |
| SDL_GetJoystickGameControllerType(const char *name, Uint16 vendor, Uint16 product, int interface_number, int interface_class, int interface_subclass, int interface_protocol) |
| { |
| static const int LIBUSB_CLASS_VENDOR_SPEC = 0xFF; |
| static const int XB360_IFACE_SUBCLASS = 93; |
| static const int XB360_IFACE_PROTOCOL = 1; /* Wired */ |
| static const int XB360W_IFACE_PROTOCOL = 129; /* Wireless */ |
| static const int XBONE_IFACE_SUBCLASS = 71; |
| static const int XBONE_IFACE_PROTOCOL = 208; |
| |
| SDL_GameControllerType type = SDL_CONTROLLER_TYPE_UNKNOWN; |
| |
| /* This code should match the checks in libusb/hid.c and HIDDeviceManager.java */ |
| if (interface_class == LIBUSB_CLASS_VENDOR_SPEC && |
| interface_subclass == XB360_IFACE_SUBCLASS && |
| (interface_protocol == XB360_IFACE_PROTOCOL || |
| interface_protocol == XB360W_IFACE_PROTOCOL)) { |
| |
| static const int SUPPORTED_VENDORS[] = { |
| 0x0079, /* GPD Win 2 */ |
| 0x044f, /* Thrustmaster */ |
| 0x045e, /* Microsoft */ |
| 0x046d, /* Logitech */ |
| 0x056e, /* Elecom */ |
| 0x06a3, /* Saitek */ |
| 0x0738, /* Mad Catz */ |
| 0x07ff, /* Mad Catz */ |
| 0x0e6f, /* PDP */ |
| 0x0f0d, /* Hori */ |
| 0x1038, /* SteelSeries */ |
| 0x11c9, /* Nacon */ |
| 0x12ab, /* Unknown */ |
| 0x1430, /* RedOctane */ |
| 0x146b, /* BigBen */ |
| 0x1532, /* Razer Sabertooth */ |
| 0x15e4, /* Numark */ |
| 0x162e, /* Joytech */ |
| 0x1689, /* Razer Onza */ |
| 0x1949, /* Lab126, Inc. */ |
| 0x1bad, /* Harmonix */ |
| 0x20d6, /* PowerA */ |
| 0x24c6, /* PowerA */ |
| }; |
| |
| int i; |
| for (i = 0; i < SDL_arraysize(SUPPORTED_VENDORS); ++i) { |
| if (vendor == SUPPORTED_VENDORS[i]) { |
| type = SDL_CONTROLLER_TYPE_XBOX360; |
| break; |
| } |
| } |
| } |
| |
| if (interface_number == 0 && |
| interface_class == LIBUSB_CLASS_VENDOR_SPEC && |
| interface_subclass == XBONE_IFACE_SUBCLASS && |
| interface_protocol == XBONE_IFACE_PROTOCOL) { |
| |
| static const int SUPPORTED_VENDORS[] = { |
| 0x045e, /* Microsoft */ |
| 0x0738, /* Mad Catz */ |
| 0x0e6f, /* PDP */ |
| 0x0f0d, /* Hori */ |
| 0x1532, /* Razer Wildcat */ |
| 0x20d6, /* PowerA */ |
| 0x24c6, /* PowerA */ |
| 0x2e24, /* Hyperkin */ |
| }; |
| |
| int i; |
| for (i = 0; i < SDL_arraysize(SUPPORTED_VENDORS); ++i) { |
| if (vendor == SUPPORTED_VENDORS[i]) { |
| type = SDL_CONTROLLER_TYPE_XBOXONE; |
| break; |
| } |
| } |
| } |
| |
| if (type == SDL_CONTROLLER_TYPE_UNKNOWN) { |
| if (vendor == 0x0000 && product == 0x0000) { |
| /* Some devices are only identifiable by their name */ |
| if (name && |
| (SDL_strcmp(name, "Lic Pro Controller") == 0 || |
| SDL_strcmp(name, "Nintendo Wireless Gamepad") == 0 || |
| SDL_strcmp(name, "Wireless Gamepad") == 0)) { |
| /* HORI or PowerA Switch Pro Controller clone */ |
| type = SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO; |
| } else if (name && SDL_strcmp(name, "Virtual Joystick") == 0) { |
| type = SDL_CONTROLLER_TYPE_VIRTUAL; |
| } else { |
| type = SDL_CONTROLLER_TYPE_UNKNOWN; |
| } |
| |
| } else if (vendor == 0x0001 && product == 0x0001) { |
| type = SDL_CONTROLLER_TYPE_UNKNOWN; |
| |
| } 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_JOY_CON_GRIP) { |
| type = SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI_JOY_CONS, SDL_FALSE) ? SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO : SDL_CONTROLLER_TYPE_UNKNOWN; |
| |
| } 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_SwitchProController: |
| case k_eControllerType_SwitchInputOnlyController: |
| type = SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO; |
| break; |
| case k_eControllerType_SwitchJoyConLeft: |
| case k_eControllerType_SwitchJoyConRight: |
| type = SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI_JOY_CONS, SDL_FALSE) ? SDL_CONTROLLER_TYPE_NINTENDO_SWITCH_PRO : SDL_CONTROLLER_TYPE_UNKNOWN; |
| break; |
| default: |
| type = SDL_CONTROLLER_TYPE_UNKNOWN; |
| break; |
| } |
| } |
| } |
| return type; |
| } |
| |
| 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; |
| } |
| } |
| 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 || |
| (vendor_id == USB_VENDOR_NINTENDO && product_id == USB_PRODUCT_NINTENDO_SWITCH_JOY_CON_GRIP)); |
| } |
| |
| 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_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), /* Logitech generic wheel */ |
| MAKE_VIDPID(0x046d, 0xc295), /* Logitech Momo Force */ |
| MAKE_VIDPID(0x046d, 0xc298), /* Logitech Driving Force Pro */ |
| MAKE_VIDPID(0x046d, 0xc299), /* Logitech G25 */ |
| MAKE_VIDPID(0x046d, 0xc29a), /* Logitech Driving Force GT */ |
| MAKE_VIDPID(0x046d, 0xc29b), /* Logitech G27 */ |
| MAKE_VIDPID(0x046d, 0xc24f), /* Logitech G29 (PS3) */ |
| MAKE_VIDPID(0x046d, 0xc260), /* Logitech G29 (PS4) */ |
| MAKE_VIDPID(0x046d, 0xc261), /* Logitech G920 (initial mode) */ |
| MAKE_VIDPID(0x046d, 0xc262), /* Logitech G920 (active mode) */ |
| MAKE_VIDPID(0x046d, 0xc26e), /* Logitech G923 */ |
| MAKE_VIDPID(0x044f, 0xb65d), /* Thrustmaster Wheel FFB */ |
| MAKE_VIDPID(0x044f, 0xb66d), /* Thrustmaster Wheel FFB */ |
| MAKE_VIDPID(0x044f, 0xb677), /* Thrustmaster T150 */ |
| MAKE_VIDPID(0x044f, 0xb66e), /* Thrustmaster T300RS */ |
| MAKE_VIDPID(0x044f, 0xb65e), /* Thrustmaster T500RS */ |
| MAKE_VIDPID(0x044f, 0xb664), /* Thrustmaster TX (initial mode) */ |
| MAKE_VIDPID(0x044f, 0xb669), /* Thrustmaster TX (active mode) */ |
| }; |
| 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), /* Venom Arcade Stick */ |
| MAKE_VIDPID(0x0f0d, 0x006a), /* Real Arcade Pro 4 */ |
| MAKE_VIDPID(0x0079, 0x181b), /* Venom Arcade Stick */ |
| MAKE_VIDPID(0x0c12, 0x0ef6), /* Hitbox Arcade Stick */ |
| MAKE_VIDPID(0x0f0d, 0x008a), /* HORI Real Arcade Pro 4 */ |
| MAKE_VIDPID(0x0f0d, 0x0016), /* Hori Real Arcade Pro.EX */ |
| MAKE_VIDPID(0x0f0d, 0x001b), /* Hori Real Arcade Pro VX */ |
| MAKE_VIDPID(0x0f0d, 0x008c), /* Hori Real Arcade Pro 4 */ |
| MAKE_VIDPID(0x1bad, 0xf03d), /* Street Fighter IV Arcade Stick TE - Chun Li */ |
| MAKE_VIDPID(0x1bad, 0xf502), /* Hori Real Arcade Pro.VX SA */ |
| MAKE_VIDPID(0x1bad, 0xf504), /* Hori Real Arcade Pro. EX */ |
| MAKE_VIDPID(0x1bad, 0xf506), /* Hori Real Arcade Pro.EX Premium VLX */ |
| MAKE_VIDPID(0x24c6, 0x5000), /* Razer Atrox Arcade Stick */ |
| MAKE_VIDPID(0x24c6, 0x5501), /* Hori Real Arcade Pro VX-SA */ |
| MAKE_VIDPID(0x24c6, 0x550e), /* Hori Real Arcade Pro V Kai 360 */ |
| MAKE_VIDPID(0x0f0d, 0x0063), /* Hori Real Arcade Pro Hayabusa (USA) Xbox One */ |
| MAKE_VIDPID(0x0f0d, 0x0078), /* Hori Real Arcade Pro V Kai Xbox One */ |
| MAKE_VIDPID(0x1532, 0x0a00), /* Razer Atrox Arcade Stick */ |
| MAKE_VIDPID(0x0f0d, 0x00aa), /* HORI Real Arcade Pro V Hayabusa in Switch Mode */ |
| MAKE_VIDPID(0x20d6, 0xa715), /* PowerA Nintendo Switch Fusion Arcade Stick */ |
| }; |
| 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), /* HOTAS Warthog Joystick */ |
| MAKE_VIDPID(0x0738, 0x2221), /* Saitek Pro Flight X-56 Rhino Stick */ |
| MAKE_VIDPID(0x044f, 0xb10a), /* ThrustMaster, Inc. T.16000M Joystick */ |
| }; |
| 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), /* HOTAS Warthog Throttle */ |
| MAKE_VIDPID(0x0738, 0xa221), /* Saitek Pro Flight X-56 Rhino Throttle */ |
| }; |
| 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)) { |
| /* XInput GUID, get the type based on the XInput device subtype */ |
| switch (guid.data[15]) { |
| case 0x01: /* XINPUT_DEVSUBTYPE_GAMEPAD */ |
| return SDL_JOYSTICK_TYPE_GAMECONTROLLER; |
| case 0x02: /* XINPUT_DEVSUBTYPE_WHEEL */ |
| return SDL_JOYSTICK_TYPE_WHEEL; |
| case 0x03: /* XINPUT_DEVSUBTYPE_ARCADE_STICK */ |
| return SDL_JOYSTICK_TYPE_ARCADE_STICK; |
| case 0x04: /* XINPUT_DEVSUBTYPE_FLIGHT_STICK */ |
| return SDL_JOYSTICK_TYPE_FLIGHT_STICK; |
| case 0x05: /* XINPUT_DEVSUBTYPE_DANCE_PAD */ |
| return SDL_JOYSTICK_TYPE_DANCE_PAD; |
| case 0x06: /* XINPUT_DEVSUBTYPE_GUITAR */ |
| case 0x07: /* XINPUT_DEVSUBTYPE_GUITAR_ALTERNATE */ |
| case 0x0B: /* XINPUT_DEVSUBTYPE_GUITAR_BASS */ |
| return SDL_JOYSTICK_TYPE_GUITAR; |
| case 0x08: /* XINPUT_DEVSUBTYPE_DRUM_KIT */ |
| return SDL_JOYSTICK_TYPE_DRUM_KIT; |
| case 0x13: /* XINPUT_DEVSUBTYPE_ARCADE_PAD */ |
| 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); |
| 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; |
| } |
| |
| if (GuessControllerType(vendor, product) != k_eControllerType_UnknownNonSteamController) { |
| return SDL_JOYSTICK_TYPE_GAMECONTROLLER; |
| } |
| |
| return SDL_JOYSTICK_TYPE_UNKNOWN; |
| } |
| |
| static SDL_bool SDL_IsPS4RemapperRunning(void) |
| { |
| #ifdef __WIN32__ |
| const char *mapper_processes[] = { |
| "DS4Windows.exe", |
| "InputMapper.exe", |
| }; |
| int i; |
| PROCESSENTRY32 pe32; |
| SDL_bool found = SDL_FALSE; |
| |
| /* Take a snapshot of all processes in the system */ |
| HANDLE hProcessSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); |
| if (hProcessSnap != INVALID_HANDLE_VALUE) { |
| pe32.dwSize = sizeof(PROCESSENTRY32); |
| if (Process32First(hProcessSnap, &pe32)) { |
| do |
| { |
| for (i = 0; i < SDL_arraysize(mapper_processes); ++i) { |
| if (SDL_strcasecmp(pe32.szExeFile, mapper_processes[i]) == 0) { |
| found = SDL_TRUE; |
| } |
| } |
| } while (Process32Next(hProcessSnap, &pe32) && !found); |
| } |
| CloseHandle(hProcessSnap); |
| } |
| return found; |
| #else |
| return SDL_FALSE; |
| #endif |
| } |
| |
| SDL_bool SDL_ShouldIgnoreJoystick(const char *name, SDL_JoystickGUID guid) |
| { |
| /* This list is taken from: |
| https://raw.githubusercontent.com/denilsonsa/udev-joystick-blacklist/master/generate_rules.py |
| */ |
| static Uint32 joystick_blacklist[] = { |
| /* Microsoft Microsoft Wireless Optical Desktop 2.10 */ |
| /* Microsoft Wireless Desktop - Comfort Edition */ |
| MAKE_VIDPID(0x045e, 0x009d), |
| |
| /* Microsoft Microsoft Digital Media Pro Keyboard */ |
| /* Microsoft Corp. Digital Media Pro Keyboard */ |
| MAKE_VIDPID(0x045e, 0x00b0), |
| |
| /* Microsoft Microsoft Digital Media Keyboard */ |
| /* Microsoft Corp. Digital Media Keyboard 1.0A */ |
| MAKE_VIDPID(0x045e, 0x00b4), |
| |
| /* Microsoft Microsoft Digital Media Keyboard 3000 */ |
| MAKE_VIDPID(0x045e, 0x0730), |
| |
| /* Microsoft Microsoft 2.4GHz Transceiver v6.0 */ |
| /* Microsoft Microsoft 2.4GHz Transceiver v8.0 */ |
| /* Microsoft Corp. Nano Transceiver v1.0 for Bluetooth */ |
| /* Microsoft Wireless Mobile Mouse 1000 */ |
| /* Microsoft Wireless Desktop 3000 */ |
| MAKE_VIDPID(0x045e, 0x0745), |
| |
| /* Microsoft SideWinder(TM) 2.4GHz Transceiver */ |
| MAKE_VIDPID(0x045e, 0x0748), |
| |
| /* Microsoft Corp. Wired Keyboard 600 */ |
| MAKE_VIDPID(0x045e, 0x0750), |
| |
| /* Microsoft Corp. Sidewinder X4 keyboard */ |
| MAKE_VIDPID(0x045e, 0x0768), |
| |
| /* Microsoft Corp. Arc Touch Mouse Transceiver */ |
| MAKE_VIDPID(0x045e, 0x0773), |
| |
| /* Microsoft 2.4GHz Transceiver v9.0 */ |
| /* Microsoft Nano Transceiver v2.1 */ |
| /* Microsoft Sculpt Ergonomic Keyboard (5KV-00001) */ |
| MAKE_VIDPID(0x045e, 0x07a5), |
| |
| /* Microsoft Nano Transceiver v1.0 */ |
| /* Microsoft Wireless Keyboard 800 */ |
| MAKE_VIDPID(0x045e, 0x07b2), |
| |
| /* Microsoft Nano Transceiver v2.0 */ |
| MAKE_VIDPID(0x045e, 0x0800), |
| |
| MAKE_VIDPID(0x046d, 0xc30a), /* Logitech, Inc. iTouch Composite keboard */ |
| |
| MAKE_VIDPID(0x04d9, 0xa0df), /* Tek Syndicate Mouse (E-Signal USB Gaming Mouse) */ |
| |
| /* List of Wacom devices at: http://linuxwacom.sourceforge.net/wiki/index.php/Device_IDs */ |
| MAKE_VIDPID(0x056a, 0x0010), /* Wacom ET-0405 Graphire */ |
| MAKE_VIDPID(0x056a, 0x0011), /* Wacom ET-0405A Graphire2 (4x5) */ |
| MAKE_VIDPID(0x056a, 0x0012), /* Wacom ET-0507A Graphire2 (5x7) */ |
| MAKE_VIDPID(0x056a, 0x0013), /* Wacom CTE-430 Graphire3 (4x5) */ |
| MAKE_VIDPID(0x056a, 0x0014), /* Wacom CTE-630 Graphire3 (6x8) */ |
| MAKE_VIDPID(0x056a, 0x0015), /* Wacom CTE-440 Graphire4 (4x5) */ |
| MAKE_VIDPID(0x056a, 0x0016), /* Wacom CTE-640 Graphire4 (6x8) */ |
| MAKE_VIDPID(0x056a, 0x0017), /* Wacom CTE-450 Bamboo Fun (4x5) */ |
| MAKE_VIDPID(0x056a, 0x0018), /* Wacom CTE-650 Bamboo Fun 6x8 */ |
| MAKE_VIDPID(0x056a, 0x0019), /* Wacom CTE-631 Bamboo One */ |
| MAKE_VIDPID(0x056a, 0x00d1), /* Wacom Bamboo Pen and Touch CTH-460 */ |
| MAKE_VIDPID(0x056a, 0x030e), /* Wacom Intuos Pen (S) CTL-480 */ |
| |
| MAKE_VIDPID(0x09da, 0x054f), /* A4 Tech Co., G7 750 mouse */ |
| MAKE_VIDPID(0x09da, 0x1410), /* A4 Tech Co., Ltd Bloody AL9 mouse */ |
| MAKE_VIDPID(0x09da, 0x3043), /* A4 Tech Co., Ltd Bloody R8A Gaming Mouse */ |
| MAKE_VIDPID(0x09da, 0x31b5), /* A4 Tech Co., Ltd Bloody TL80 Terminator Laser Gaming Mouse */ |
| MAKE_VIDPID(0x09da, 0x3997), /* A4 Tech Co., Ltd Bloody RT7 Terminator Wireless */ |
| MAKE_VIDPID(0x09da, 0x3f8b), /* A4 Tech Co., Ltd Bloody V8 mouse */ |
| MAKE_VIDPID(0x09da, 0x51f4), /* Modecom MC-5006 Keyboard */ |
| MAKE_VIDPID(0x09da, 0x5589), /* A4 Tech Co., Ltd Terminator TL9 Laser Gaming Mouse */ |
| MAKE_VIDPID(0x09da, 0x7b22), /* A4 Tech Co., Ltd Bloody V5 */ |
| MAKE_VIDPID(0x09da, 0x7f2d), /* A4 Tech Co., Ltd Bloody R3 mouse */ |
| MAKE_VIDPID(0x09da, 0x8090), /* A4 Tech Co., Ltd X-718BK Oscar Optical Gaming Mouse */ |
| MAKE_VIDPID(0x09da, 0x9033), /* A4 Tech Co., X7 X-705K */ |
| MAKE_VIDPID(0x09da, 0x9066), /* A4 Tech Co., Sharkoon Fireglider Optical */ |
| MAKE_VIDPID(0x09da, 0x9090), /* A4 Tech Co., Ltd XL-730K / XL-750BK / XL-755BK Laser Mouse */ |
| MAKE_VIDPID(0x09da, 0x90c0), /* A4 Tech Co., Ltd X7 G800V keyboard */ |
| MAKE_VIDPID(0x09da, 0xf012), /* A4 Tech Co., Ltd Bloody V7 mouse */ |
| MAKE_VIDPID(0x09da, 0xf32a), /* A4 Tech Co., Ltd Bloody B540 keyboard */ |
| MAKE_VIDPID(0x09da, 0xf613), /* A4 Tech Co., Ltd Bloody V2 mouse */ |
| MAKE_VIDPID(0x09da, 0xf624), /* A4 Tech Co., Ltd Bloody B120 Keyboard */ |
| |
| MAKE_VIDPID(0x1b1c, 0x1b3c), /* Corsair Harpoon RGB gaming mouse */ |
| |
| MAKE_VIDPID(0x1d57, 0xad03), /* [T3] 2.4GHz and IR Air Mouse Remote Control */ |
| |
| MAKE_VIDPID(0x1e7d, 0x2e4a), /* Roccat Tyon Mouse */ |
| |
| MAKE_VIDPID(0x20a0, 0x422d), /* Winkeyless.kr Keyboards */ |
| |
| MAKE_VIDPID(0x2516, 0x001f), /* Cooler Master Storm Mizar Mouse */ |
| MAKE_VIDPID(0x2516, 0x0028), /* Cooler Master Storm Alcor Mouse */ |
| |
| /*****************************************************************/ |
| /* Additional entries */ |
| /*****************************************************************/ |
| |
| MAKE_VIDPID(0x04d9, 0x8009), /* OBINLB USB-HID Keyboard */ |
| MAKE_VIDPID(0x0b05, 0x1958), /* ROG Chakram Mouse */ |
| MAKE_VIDPID(0x26ce, 0x01a2), /* ASRock LED Controller */ |
| }; |
| |
| unsigned int i; |
| Uint32 id; |
| Uint16 vendor; |
| Uint16 product; |
| SDL_GameControllerType type; |
| |
| SDL_GetJoystickGUIDInfo(guid, &vendor, &product, NULL); |
| |
| /* Check the joystick blacklist */ |
| id = MAKE_VIDPID(vendor, product); |
| for (i = 0; i < SDL_arraysize(joystick_blacklist); ++i) { |
| if (id == joystick_blacklist[i]) { |
| return SDL_TRUE; |
| } |
| } |
| |
| type = SDL_GetJoystickGameControllerType(name, vendor, product, -1, 0, 0, 0); |
| if ((type == SDL_CONTROLLER_TYPE_PS4 || type == SDL_CONTROLLER_TYPE_PS5) && SDL_IsPS4RemapperRunning()) { |
| return SDL_TRUE; |
| } |
| |
| if (SDL_IsGameControllerNameAndGUID(name, guid) && |
| SDL_ShouldIgnoreGameController(name, guid)) { |
| return SDL_TRUE; |
| } |
| |
| return SDL_FALSE; |
| } |
| |
| /* return the guid for this index */ |
| 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); |
| return vendor; |
| } |
| |
| Uint16 SDL_JoystickGetDeviceProduct(int device_index) |
| { |
| Uint16 product; |
| SDL_JoystickGUID guid = SDL_JoystickGetDeviceGUID(device_index); |
| |
| SDL_GetJoystickGUIDInfo(guid, NULL, &product, NULL); |
| return product; |
| } |
| |
| Uint16 SDL_JoystickGetDeviceProductVersion(int device_index) |
| { |
| Uint16 version; |
| SDL_JoystickGUID guid = SDL_JoystickGetDeviceGUID(device_index); |
| |
| SDL_GetJoystickGUIDInfo(guid, NULL, NULL, &version); |
| 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) |
| { |
| if (!SDL_PrivateJoystickValid(joystick)) { |
| SDL_JoystickGUID emptyGUID; |
| SDL_zero(emptyGUID); |
| return emptyGUID; |
| } |
| return joystick->guid; |
| } |
| |
| Uint16 SDL_JoystickGetVendor(SDL_Joystick *joystick) |
| { |
| Uint16 vendor; |
| SDL_JoystickGUID guid = SDL_JoystickGetGUID(joystick); |
| |
| SDL_GetJoystickGUIDInfo(guid, &vendor, NULL, NULL); |
| return vendor; |
| } |
| |
| Uint16 SDL_JoystickGetProduct(SDL_Joystick *joystick) |
| { |
| Uint16 product; |
| SDL_JoystickGUID guid = SDL_JoystickGetGUID(joystick); |
| |
| SDL_GetJoystickGUIDInfo(guid, NULL, &product, NULL); |
| return product; |
| } |
| |
| Uint16 SDL_JoystickGetProductVersion(SDL_Joystick *joystick) |
| { |
| Uint16 version; |
| SDL_JoystickGUID guid = SDL_JoystickGetGUID(joystick); |
| |
| SDL_GetJoystickGUIDInfo(guid, NULL, NULL, &version); |
| return version; |
| } |
| |
| const char *SDL_JoystickGetSerial(SDL_Joystick *joystick) |
| { |
| if (!SDL_PrivateJoystickValid(joystick)) { |
| return 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; |
| } |
| |
| /* convert the guid to a printable string */ |
| void SDL_JoystickGetGUIDString(SDL_JoystickGUID guid, char *pszGUID, int cbGUID) |
| { |
| static const char k_rgchHexToASCII[] = "0123456789abcdef"; |
| int i; |
| |
| if ((pszGUID == NULL) || (cbGUID <= 0)) { |
| return; |
| } |
| |
| for (i = 0; i < sizeof(guid.data) && i < (cbGUID-1)/2; i++) { |
| /* each input byte writes 2 ascii chars, and might write a null byte. */ |
| /* If we don't have room for next input byte, stop */ |
| unsigned char c = guid.data[i]; |
| |
| *pszGUID++ = k_rgchHexToASCII[c >> 4]; |
| *pszGUID++ = k_rgchHexToASCII[c & 0x0F]; |
| } |
| *pszGUID = '\0'; |
| } |
| |
| /*----------------------------------------------------------------------------- |
| * Purpose: Returns the 4 bit nibble for a hex character |
| * Input : c - |
| * Output : unsigned char |
| *-----------------------------------------------------------------------------*/ |
| static unsigned char nibble(char c) |
| { |
| if ((c >= '0') && (c <= '9')) { |
| return (unsigned char)(c - '0'); |
| } |
| |
| if ((c >= 'A') && (c <= 'F')) { |
| return (unsigned char)(c - 'A' + 0x0a); |
| } |
| |
| if ((c >= 'a') && (c <= 'f')) { |
| return (unsigned char)(c - 'a' + 0x0a); |
| } |
| |
| /* received an invalid character, and no real way to return an error */ |
| /* AssertMsg1(false, "Q_nibble invalid hex character '%c' ", c); */ |
| return 0; |
| } |
| |
| /* convert the string version of a joystick guid to the struct */ |
| SDL_JoystickGUID SDL_JoystickGetGUIDFromString(const char *pchGUID) |
| { |
| SDL_JoystickGUID guid; |
| int maxoutputbytes= sizeof(guid); |
| size_t len = SDL_strlen(pchGUID); |
| Uint8 *p; |
| size_t i; |
| |
| /* Make sure it's even */ |
| len = (len) & ~0x1; |
| |
| SDL_memset(&guid, 0x00, sizeof(guid)); |
| |
| p = (Uint8 *)&guid; |
| for (i = 0; (i < len) && ((p - (Uint8 *)&guid) < maxoutputbytes); i+=2, p++) { |
| *p = (nibble(pchGUID[i]) << 4) | nibble(pchGUID[i+1]); |
| } |
| |
| return guid; |
| } |
| |
| /* update the power level for this joystick */ |
| void SDL_PrivateJoystickBatteryLevel(SDL_Joystick *joystick, SDL_JoystickPowerLevel ePowerLevel) |
| { |
| joystick->epowerlevel = ePowerLevel; |
| } |
| |
| /* return its power level */ |
| SDL_JoystickPowerLevel SDL_JoystickCurrentPowerLevel(SDL_Joystick *joystick) |
| { |
| if (!SDL_PrivateJoystickValid(joystick)) { |
| return 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; |
| #if !SDL_EVENTS_DISABLED |
| Uint32 event_type; |
| #endif |
| |
| 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 !SDL_EVENTS_DISABLED |
| if (state == finger_info->state) { |
| event_type = SDL_CONTROLLERTOUCHPADMOTION; |
| } else if (state) { |
| event_type = SDL_CONTROLLERTOUCHPADDOWN; |
| } else { |
| event_type = SDL_CONTROLLERTOUCHPADUP; |
| } |
| #endif |
| |
| /* We ignore events if we don't have keyboard focus, except for touch release */ |
| if (SDL_PrivateJoystickShouldIgnoreEvent()) { |
| if (event_type != SDL_CONTROLLERTOUCHPADUP) { |
| return 0; |
| } |
| } |
| |
| /* Update internal joystick state */ |
| finger_info->state = state; |
| finger_info->x = x; |
| finger_info->y = y; |
| finger_info->pressure = pressure; |
| |
| /* Post the event, if desired */ |
| 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 /* !SDL_EVENTS_DISABLED */ |
| return posted; |
| } |
| |
| int SDL_PrivateJoystickSensor(SDL_Joystick *joystick, SDL_SensorType type, const float *data, int num_values) |
| { |
| int i; |
| int posted = 0; |
| |
| /* We ignore events if we don't have keyboard focus */ |
| 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)); |
| |
| /* Update internal sensor state */ |
| SDL_memcpy(sensor->data, data, num_values*sizeof(*data)); |
| |
| /* Post the event, if desired */ |
| #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)); |
| posted = SDL_PushEvent(&event) == 1; |
| } |
| #endif /* !SDL_EVENTS_DISABLED */ |
| } |
| break; |
| } |
| } |
| return posted; |
| } |
| |
| /* vi: set ts=4 sw=4 expandtab: */ |