diff --git a/Makefile.os2 b/Makefile.os2 index 31cd03a74..2f8d5ff34 100644 --- a/Makefile.os2 +++ b/Makefile.os2 @@ -89,7 +89,7 @@ SRCS+= SDL_systimer.c SRCS+= SDL_sysloadso.c SRCS+= SDL_sysfilesystem.c SRCS+= SDL_os2joystick.c SDL_syshaptic.c SDL_sysjoystick.c SDL_virtualjoystick.c -SRCS+= SDL_hidapijoystick.c SDL_hidapi_rumble.c SDL_hidapi_gamecube.c SDL_hidapi_luna.c SDL_hidapi_ps4.c SDL_hidapi_ps5.c SDL_hidapi_stadia.c SDL_hidapi_switch.c SDL_hidapi_xbox360.c SDL_hidapi_xbox360w.c SDL_hidapi_xboxone.c SDL_hidapi_steam.c +SRCS+= SDL_hidapijoystick.c SDL_hidapi_rumble.c SDL_hidapi_gamecube.c SDL_hidapi_luna.c SDL_hidapi_ps4.c SDL_hidapi_ps5.c SDL_hidapi_shield.c SDL_hidapi_stadia.c SDL_hidapi_switch.c SDL_hidapi_xbox360.c SDL_hidapi_xbox360w.c SDL_hidapi_xboxone.c SDL_hidapi_steam.c SRCS+= SDL_dummyaudio.c SDL_diskaudio.c SRCS+= SDL_nullvideo.c SDL_nullframebuffer.c SDL_nullevents.c SRCS+= SDL_dummysensor.c diff --git a/Makefile.w32 b/Makefile.w32 index 8c9c22e63..1ca1fa2ea 100644 --- a/Makefile.w32 +++ b/Makefile.w32 @@ -65,7 +65,7 @@ SRCS+= SDL_systimer.c SRCS+= SDL_sysloadso.c SRCS+= SDL_sysfilesystem.c SRCS+= SDL_syshaptic.c SDL_sysjoystick.c SDL_virtualjoystick.c -SRCS+= SDL_hidapijoystick.c SDL_hidapi_rumble.c SDL_hidapi_gamecube.c SDL_hidapi_luna.c SDL_hidapi_ps4.c SDL_hidapi_ps5.c SDL_hidapi_stadia.c SDL_hidapi_switch.c SDL_hidapi_xbox360.c SDL_hidapi_xbox360w.c SDL_hidapi_xboxone.c SDL_hidapi_steam.c +SRCS+= SDL_hidapijoystick.c SDL_hidapi_rumble.c SDL_hidapi_gamecube.c SDL_hidapi_luna.c SDL_hidapi_ps4.c SDL_hidapi_ps5.c SDL_hidapi_shield.c SDL_hidapi_stadia.c SDL_hidapi_switch.c SDL_hidapi_xbox360.c SDL_hidapi_xbox360w.c SDL_hidapi_xboxone.c SDL_hidapi_steam.c SRCS+= SDL_dummyaudio.c SDL_diskaudio.c SRCS+= SDL_nullvideo.c SDL_nullframebuffer.c SDL_nullevents.c SRCS+= SDL_dummysensor.c diff --git a/VisualC-GDK/SDL/SDL.vcxproj b/VisualC-GDK/SDL/SDL.vcxproj index c143597a0..1d31c8f32 100644 --- a/VisualC-GDK/SDL/SDL.vcxproj +++ b/VisualC-GDK/SDL/SDL.vcxproj @@ -595,6 +595,7 @@ + diff --git a/VisualC-GDK/SDL/SDL.vcxproj.filters b/VisualC-GDK/SDL/SDL.vcxproj.filters index d005dead6..71aeb2400 100644 --- a/VisualC-GDK/SDL/SDL.vcxproj.filters +++ b/VisualC-GDK/SDL/SDL.vcxproj.filters @@ -1065,6 +1065,9 @@ joystick\hidapi + + joystick\hidapi + joystick\hidapi diff --git a/VisualC/SDL/SDL.vcxproj b/VisualC/SDL/SDL.vcxproj index 8102c7fbd..418a18f6c 100644 --- a/VisualC/SDL/SDL.vcxproj +++ b/VisualC/SDL/SDL.vcxproj @@ -486,6 +486,7 @@ + diff --git a/VisualC/SDL/SDL.vcxproj.filters b/VisualC/SDL/SDL.vcxproj.filters index 16b717156..0e23dc505 100644 --- a/VisualC/SDL/SDL.vcxproj.filters +++ b/VisualC/SDL/SDL.vcxproj.filters @@ -1058,6 +1058,9 @@ joystick\hidapi + + joystick\hidapi + joystick\hidapi diff --git a/Xcode/SDL/SDL.xcodeproj/project.pbxproj b/Xcode/SDL/SDL.xcodeproj/project.pbxproj index 1c293410a..174db617c 100644 --- a/Xcode/SDL/SDL.xcodeproj/project.pbxproj +++ b/Xcode/SDL/SDL.xcodeproj/project.pbxproj @@ -93,6 +93,15 @@ 75E09169241EA924004729E1 /* SDL_virtualjoystick_c.h in Headers */ = {isa = PBXBuildFile; fileRef = 75E09159241EA924004729E1 /* SDL_virtualjoystick_c.h */; }; 75E0916A241EA924004729E1 /* SDL_virtualjoystick_c.h in Headers */ = {isa = PBXBuildFile; fileRef = 75E09159241EA924004729E1 /* SDL_virtualjoystick_c.h */; }; 75E0916B241EA924004729E1 /* SDL_virtualjoystick_c.h in Headers */ = {isa = PBXBuildFile; fileRef = 75E09159241EA924004729E1 /* SDL_virtualjoystick_c.h */; }; + 9846B07C287A9020000C35C8 /* SDL_hidapi_shield.c in Sources */ = {isa = PBXBuildFile; fileRef = 9846B07B287A9020000C35C8 /* SDL_hidapi_shield.c */; }; + 9846B07D287A9020000C35C8 /* SDL_hidapi_shield.c in Sources */ = {isa = PBXBuildFile; fileRef = 9846B07B287A9020000C35C8 /* SDL_hidapi_shield.c */; }; + 9846B07E287A9020000C35C8 /* SDL_hidapi_shield.c in Sources */ = {isa = PBXBuildFile; fileRef = 9846B07B287A9020000C35C8 /* SDL_hidapi_shield.c */; }; + 9846B07F287A9020000C35C8 /* SDL_hidapi_shield.c in Sources */ = {isa = PBXBuildFile; fileRef = 9846B07B287A9020000C35C8 /* SDL_hidapi_shield.c */; }; + 9846B080287A9020000C35C8 /* SDL_hidapi_shield.c in Sources */ = {isa = PBXBuildFile; fileRef = 9846B07B287A9020000C35C8 /* SDL_hidapi_shield.c */; }; + 9846B081287A9020000C35C8 /* SDL_hidapi_shield.c in Sources */ = {isa = PBXBuildFile; fileRef = 9846B07B287A9020000C35C8 /* SDL_hidapi_shield.c */; }; + 9846B082287A9020000C35C8 /* SDL_hidapi_shield.c in Sources */ = {isa = PBXBuildFile; fileRef = 9846B07B287A9020000C35C8 /* SDL_hidapi_shield.c */; }; + 9846B083287A9020000C35C8 /* SDL_hidapi_shield.c in Sources */ = {isa = PBXBuildFile; fileRef = 9846B07B287A9020000C35C8 /* SDL_hidapi_shield.c */; }; + 9846B084287A9020000C35C8 /* SDL_hidapi_shield.c in Sources */ = {isa = PBXBuildFile; fileRef = 9846B07B287A9020000C35C8 /* SDL_hidapi_shield.c */; }; A1626A3E2617006A003F1973 /* SDL_triangle.c in Sources */ = {isa = PBXBuildFile; fileRef = A1626A3D2617006A003F1973 /* SDL_triangle.c */; }; A1626A3F2617006A003F1973 /* SDL_triangle.c in Sources */ = {isa = PBXBuildFile; fileRef = A1626A3D2617006A003F1973 /* SDL_triangle.c */; }; A1626A402617006A003F1973 /* SDL_triangle.c in Sources */ = {isa = PBXBuildFile; fileRef = A1626A3D2617006A003F1973 /* SDL_triangle.c */; }; @@ -3629,6 +3638,7 @@ 5C2EF7001FC9EF0F003F5197 /* SDL_egl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_egl.h; sourceTree = ""; }; 75E09158241EA924004729E1 /* SDL_virtualjoystick.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_virtualjoystick.c; sourceTree = ""; }; 75E09159241EA924004729E1 /* SDL_virtualjoystick_c.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_virtualjoystick_c.h; sourceTree = ""; }; + 9846B07B287A9020000C35C8 /* SDL_hidapi_shield.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_hidapi_shield.c; sourceTree = ""; }; A1626A3D2617006A003F1973 /* SDL_triangle.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_triangle.c; sourceTree = ""; }; A1626A512617008C003F1973 /* SDL_triangle.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDL_triangle.h; sourceTree = ""; }; A1BB8B6127F6CF320057CFA8 /* SDL_list.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = SDL_list.c; sourceTree = ""; }; @@ -4902,6 +4912,7 @@ A7D8A7BE23E2513E00DCD162 /* hidapi */ = { isa = PBXGroup; children = ( + 9846B07B287A9020000C35C8 /* SDL_hidapi_shield.c */, A7D8A7C923E2513E00DCD162 /* SDL_hidapi_gamecube.c */, F3F07D59269640160074468B /* SDL_hidapi_luna.c */, A7D8A7C323E2513E00DCD162 /* SDL_hidapi_ps4.c */, @@ -7631,6 +7642,7 @@ A75FCE3B23E25AB700529352 /* SDL_wave.c in Sources */, A75FCE3C23E25AB700529352 /* s_tan.c in Sources */, A75FCE3D23E25AB700529352 /* SDL_hints.c in Sources */, + 9846B083287A9020000C35C8 /* SDL_hidapi_shield.c in Sources */, A75FCE3E23E25AB700529352 /* SDL_hidapi_ps4.c in Sources */, A75FCE3F23E25AB700529352 /* SDL_pixels.c in Sources */, A75FCE4123E25AB700529352 /* SDL_sysloadso.c in Sources */, @@ -7823,6 +7835,7 @@ A75FCFF423E25AC700529352 /* SDL_wave.c in Sources */, A75FCFF523E25AC700529352 /* s_tan.c in Sources */, A75FCFF623E25AC700529352 /* SDL_hints.c in Sources */, + 9846B084287A9020000C35C8 /* SDL_hidapi_shield.c in Sources */, A75FCFF723E25AC700529352 /* SDL_hidapi_ps4.c in Sources */, A75FCFF823E25AC700529352 /* SDL_pixels.c in Sources */, A75FCFFA23E25AC700529352 /* SDL_sysloadso.c in Sources */, @@ -8001,6 +8014,7 @@ A769B1BE23E259AE00872273 /* SDL_error.c in Sources */, A769B1BF23E259AE00872273 /* SDL_blit.c in Sources */, A769B1C023E259AE00872273 /* SDL_rwops.c in Sources */, + 9846B081287A9020000C35C8 /* SDL_hidapi_shield.c in Sources */, A769B1C123E259AE00872273 /* SDL_uikitviewcontroller.m in Sources */, A769B1C223E259AE00872273 /* s_cos.c in Sources */, A769B1C323E259AE00872273 /* SDL_steamcontroller.c in Sources */, @@ -8208,6 +8222,7 @@ A7D8B54023E2514300DCD162 /* SDL_hidapi_ps4.c in Sources */, A7D8AD6F23E2514100DCD162 /* SDL_pixels.c in Sources */, 5616CA52252BB35A005D5928 /* SDL_url.c in Sources */, + 9846B07D287A9020000C35C8 /* SDL_hidapi_shield.c in Sources */, A7D8B75F23E2514300DCD162 /* SDL_sysloadso.c in Sources */, A7D8B5F423E2514300DCD162 /* SDL_syspower.c in Sources */, A7D8B95123E2514400DCD162 /* SDL_iconv.c in Sources */, @@ -8400,6 +8415,7 @@ A7D8B54123E2514300DCD162 /* SDL_hidapi_ps4.c in Sources */, A7D8AD7023E2514100DCD162 /* SDL_pixels.c in Sources */, 5616CA55252BB35B005D5928 /* SDL_url.c in Sources */, + 9846B07E287A9020000C35C8 /* SDL_hidapi_shield.c in Sources */, A7D8B76023E2514300DCD162 /* SDL_sysloadso.c in Sources */, A7D8B5F523E2514300DCD162 /* SDL_syspower.c in Sources */, A7D8B95223E2514400DCD162 /* SDL_iconv.c in Sources */, @@ -8578,6 +8594,7 @@ A7D8B8E823E2514400DCD162 /* SDL_error.c in Sources */, A7D8AD6C23E2514100DCD162 /* SDL_blit.c in Sources */, A7D8B5C123E2514300DCD162 /* SDL_rwops.c in Sources */, + 9846B080287A9020000C35C8 /* SDL_hidapi_shield.c in Sources */, A7D8ACD323E2514100DCD162 /* SDL_uikitviewcontroller.m in Sources */, A7D8BA9523E2514400DCD162 /* s_cos.c in Sources */, A7D8B4D423E2514300DCD162 /* SDL_steamcontroller.c in Sources */, @@ -8699,6 +8716,7 @@ A7D8B62F23E2514300DCD162 /* SDL_sysfilesystem.m in Sources */, A7D8BAC723E2514500DCD162 /* e_pow.c in Sources */, A7D8B41C23E2514300DCD162 /* SDL_systls.c in Sources */, + 9846B07C287A9020000C35C8 /* SDL_hidapi_shield.c in Sources */, A7D8BBD923E2574800DCD162 /* SDL_uikitmessagebox.m in Sources */, A7D8AD2923E2514100DCD162 /* SDL_vulkan_utils.c in Sources */, A7D8A95123E2514000DCD162 /* SDL_spinlock.c in Sources */, @@ -8889,6 +8907,7 @@ A7D8AE7F23E2514100DCD162 /* SDL_yuv.c in Sources */, A7D8B63223E2514300DCD162 /* SDL_sysfilesystem.m in Sources */, A7D8BACA23E2514500DCD162 /* e_pow.c in Sources */, + 9846B07F287A9020000C35C8 /* SDL_hidapi_shield.c in Sources */, A7D8B41F23E2514300DCD162 /* SDL_systls.c in Sources */, A7D8AD2C23E2514100DCD162 /* SDL_vulkan_utils.c in Sources */, A7D8A95423E2514000DCD162 /* SDL_spinlock.c in Sources */, @@ -9079,6 +9098,7 @@ A7D8B63423E2514300DCD162 /* SDL_sysfilesystem.m in Sources */, A7D8BACC23E2514500DCD162 /* e_pow.c in Sources */, A7D8B42123E2514300DCD162 /* SDL_systls.c in Sources */, + 9846B082287A9020000C35C8 /* SDL_hidapi_shield.c in Sources */, A7D8AD2E23E2514100DCD162 /* SDL_vulkan_utils.c in Sources */, A7D8A95623E2514000DCD162 /* SDL_spinlock.c in Sources */, A7D8BAB423E2514400DCD162 /* s_atan.c in Sources */, diff --git a/include/SDL_gamecontroller.h b/include/SDL_gamecontroller.h index 0f3ce583a..0ef0090ad 100644 --- a/include/SDL_gamecontroller.h +++ b/include/SDL_gamecontroller.h @@ -69,7 +69,8 @@ typedef enum SDL_CONTROLLER_TYPE_VIRTUAL, SDL_CONTROLLER_TYPE_PS5, SDL_CONTROLLER_TYPE_AMAZON_LUNA, - SDL_CONTROLLER_TYPE_GOOGLE_STADIA + SDL_CONTROLLER_TYPE_GOOGLE_STADIA, + SDL_CONTROLLER_TYPE_NVIDIA_SHIELD } SDL_GameControllerType; typedef enum diff --git a/include/SDL_hints.h b/include/SDL_hints.h index 4a0e553ae..44d7bc422 100644 --- a/include/SDL_hints.h +++ b/include/SDL_hints.h @@ -674,6 +674,17 @@ extern "C" { */ #define SDL_HINT_JOYSTICK_HIDAPI_LUNA "SDL_JOYSTICK_HIDAPI_LUNA" +/** + * \brief A variable controlling whether the HIDAPI driver for NVIDIA SHIELD controllers should be used. + * + * This variable can be set to the following values: + * "0" - HIDAPI driver is not used + * "1" - HIDAPI driver is used + * + * The default is the value of SDL_HINT_JOYSTICK_HIDAPI + */ +#define SDL_HINT_JOYSTICK_HIDAPI_SHIELD "SDL_JOYSTICK_HIDAPI_SHIELD" + /** * \brief A variable controlling whether the HIDAPI driver for PS4 controllers should be used. * diff --git a/src/joystick/SDL_gamecontroller.c b/src/joystick/SDL_gamecontroller.c index b667e8dcb..3a5c9c7d5 100644 --- a/src/joystick/SDL_gamecontroller.c +++ b/src/joystick/SDL_gamecontroller.c @@ -634,6 +634,10 @@ static ControllerMapping_t *SDL_CreateMappingForHIDAPIController(SDL_JoystickGUI /* The Google Stadia controller has a share button and a Google Assistant button */ SDL_strlcat(mapping_string, "misc1:b15,", sizeof(mapping_string)); break; + case SDL_CONTROLLER_TYPE_NVIDIA_SHIELD: + /* The NVIDIA SHIELD controller has a home button between back and start buttons */ + SDL_strlcat(mapping_string, "misc1:b15,", sizeof(mapping_string)); + break; default: if (vendor == 0 && product == 0) { /* This is a Bluetooth Nintendo Switch Pro controller */ diff --git a/src/joystick/SDL_joystick.c b/src/joystick/SDL_joystick.c index 0eb600ac8..5ce191fcb 100644 --- a/src/joystick/SDL_joystick.c +++ b/src/joystick/SDL_joystick.c @@ -1957,6 +1957,9 @@ SDL_GetJoystickGameControllerTypeFromVIDPID(Uint16 vendor, Uint16 product, const } else if (vendor == USB_VENDOR_GOOGLE && product == USB_PRODUCT_GOOGLE_STADIA_CONTROLLER) { type = SDL_CONTROLLER_TYPE_GOOGLE_STADIA; + } else if (vendor == USB_VENDOR_NVIDIA && product == USB_PRODUCT_NVIDIA_SHIELD_CONTROLLER) { + type = SDL_CONTROLLER_TYPE_NVIDIA_SHIELD; + } 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; diff --git a/src/joystick/hidapi/SDL_hidapi_shield.c b/src/joystick/hidapi/SDL_hidapi_shield.c new file mode 100644 index 000000000..9f6519948 --- /dev/null +++ b/src/joystick/hidapi/SDL_hidapi_shield.c @@ -0,0 +1,443 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2022 Sam Lantinga + + 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" + +#ifdef SDL_JOYSTICK_HIDAPI + +#include "SDL_hints.h" +#include "SDL_events.h" +#include "SDL_timer.h" +#include "SDL_joystick.h" +#include "SDL_gamecontroller.h" +#include "../SDL_sysjoystick.h" +#include "SDL_hidapijoystick_c.h" +#include "SDL_hidapi_rumble.h" + + +#ifdef SDL_JOYSTICK_HIDAPI_SHIELD + +/* Define this if you want to log all packets from the controller */ +/*#define DEBUG_SHIELD_PROTOCOL*/ + +#define CMD_BATTERY_STATE 0x07 +#define CMD_RUMBLE 0x39 +#define CMD_CHARGE_STATE 0x3A + +/* Milliseconds between polls of battery state */ +#define BATTERY_POLL_INTERVAL_MS 60000 + +/* Milliseconds between retransmission of rumble to keep motors running */ +#define RUMBLE_REFRESH_INTERVAL_MS 500 + +/* Reports that are too small are dropped over Bluetooth */ +#define HID_REPORT_SIZE 33 + +enum +{ + SDL_CONTROLLER_BUTTON_SHIELD_HOME = 15, + SDL_CONTROLLER_NUM_SHIELD_BUTTONS, +}; + +typedef struct { + Uint8 seq_num; + + SDL_JoystickPowerLevel battery_level; + SDL_bool charging; + Uint32 last_battery_query_time; + + SDL_bool rumble_report_pending; + SDL_bool rumble_update_pending; + Uint8 left_motor_amplitude; + Uint8 right_motor_amplitude; + Uint32 last_rumble_time; + + Uint8 last_state[USB_PACKET_LENGTH]; +} SDL_DriverShield_Context; + + +static SDL_bool +HIDAPI_DriverShield_IsSupportedDevice(const char *name, SDL_GameControllerType type, Uint16 vendor_id, Uint16 product_id, Uint16 version, int interface_number, int interface_class, int interface_subclass, int interface_protocol) +{ + return (type == SDL_CONTROLLER_TYPE_NVIDIA_SHIELD) ? SDL_TRUE : SDL_FALSE; +} + +static const char * +HIDAPI_DriverShield_GetDeviceName(const char *name, Uint16 vendor_id, Uint16 product_id) +{ + return "NVIDIA SHIELD Controller"; +} + +static SDL_bool +HIDAPI_DriverShield_InitDevice(SDL_HIDAPI_Device *device) +{ + return HIDAPI_JoystickConnected(device, NULL); +} + +static int +HIDAPI_DriverShield_GetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id) +{ + return -1; +} + +static void +HIDAPI_DriverShield_SetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL_JoystickID instance_id, int player_index) +{ +} + +static int +HIDAPI_DriverShield_SendCommand(SDL_HIDAPI_Device *device, Uint8 cmd, Uint8* data, int size) +{ + SDL_DriverShield_Context *ctx = device->context; + Uint8 cmd_pkt[HID_REPORT_SIZE]; + + if (size >= sizeof(cmd_pkt) - 3) { + return SDL_SetError("Invalid command data"); + } + + if (SDL_HIDAPI_LockRumble() < 0) { + return -1; + } + + cmd_pkt[0] = 0x04; + cmd_pkt[1] = cmd; + cmd_pkt[2] = ctx->seq_num++; + if (data) { + SDL_memcpy(&cmd_pkt[3], data, size); + } + + if (SDL_HIDAPI_SendRumbleAndUnlock(device, cmd_pkt, sizeof(cmd_pkt)) != sizeof(cmd_pkt)) { + return SDL_SetError("Couldn't send command packet"); + } + + return 0; +} + +static SDL_bool +HIDAPI_DriverShield_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick) +{ + SDL_DriverShield_Context *ctx; + + ctx = (SDL_DriverShield_Context *)SDL_calloc(1, sizeof(*ctx)); + if (!ctx) { + SDL_OutOfMemory(); + return SDL_FALSE; + } + + device->dev = SDL_hid_open_path(device->path, 0); + if (!device->dev) { + SDL_SetError("Couldn't open %s", device->path); + SDL_free(ctx); + return SDL_FALSE; + } + device->context = ctx; + + /* Initialize the joystick capabilities */ + joystick->nbuttons = SDL_CONTROLLER_NUM_SHIELD_BUTTONS; + joystick->naxes = SDL_CONTROLLER_AXIS_MAX; + joystick->epowerlevel = SDL_JOYSTICK_POWER_UNKNOWN; + + /* Request battery and charging info */ + ctx->last_battery_query_time = SDL_GetTicks(); + HIDAPI_DriverShield_SendCommand(device, CMD_CHARGE_STATE, NULL, 0); + HIDAPI_DriverShield_SendCommand(device, CMD_BATTERY_STATE, NULL, 0); + + return SDL_TRUE; +} + +static int +HIDAPI_DriverShield_SendNextRumble(SDL_HIDAPI_Device *device) +{ + SDL_DriverShield_Context *ctx = device->context; + Uint8 rumble_data[3]; + + if (!ctx->rumble_update_pending) { + return 0; + } + + rumble_data[0] = 0x01; /* enable */ + rumble_data[1] = ctx->left_motor_amplitude; + rumble_data[2] = ctx->right_motor_amplitude; + + ctx->rumble_update_pending = SDL_FALSE; + ctx->last_rumble_time = SDL_GetTicks(); + + return HIDAPI_DriverShield_SendCommand(device, CMD_RUMBLE, rumble_data, sizeof(rumble_data)); +} + +static int +HIDAPI_DriverShield_RumbleJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble) +{ + SDL_DriverShield_Context *ctx = device->context; + + /* The rumble motors are quite intense, so tone down the intensity like the official driver does */ + ctx->left_motor_amplitude = low_frequency_rumble >> 11; + ctx->right_motor_amplitude = high_frequency_rumble >> 11; + ctx->rumble_update_pending = SDL_TRUE; + + if (ctx->rumble_report_pending) { + /* We will service this after the hardware acknowledges the previous request */ + return 0; + } + + return HIDAPI_DriverShield_SendNextRumble(device); +} + +static int +HIDAPI_DriverShield_RumbleJoystickTriggers(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 left_rumble, Uint16 right_rumble) +{ + return SDL_Unsupported(); +} + +static Uint32 +HIDAPI_DriverShield_GetJoystickCapabilities(SDL_HIDAPI_Device *device, SDL_Joystick *joystick) +{ + return SDL_JOYCAP_RUMBLE; +} + +static int +HIDAPI_DriverShield_SetJoystickLED(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint8 red, Uint8 green, Uint8 blue) +{ + return SDL_Unsupported(); +} + +static int +HIDAPI_DriverShield_SendJoystickEffect(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, const void *data, int size) +{ + return SDL_Unsupported(); +} + +static int +HIDAPI_DriverShield_SetJoystickSensorsEnabled(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, SDL_bool enabled) +{ + return SDL_Unsupported(); +} + +static void +HIDAPI_DriverShield_HandleStatePacket(SDL_Joystick *joystick, SDL_DriverShield_Context *ctx, Uint8 *data, int size) +{ + if (size < 23) { + return; + } + + if (ctx->last_state[2] != data[2]) { + SDL_bool dpad_up = SDL_FALSE; + SDL_bool dpad_down = SDL_FALSE; + SDL_bool dpad_left = SDL_FALSE; + SDL_bool dpad_right = SDL_FALSE; + + switch (data[2]) { + case 0: + dpad_up = SDL_TRUE; + break; + case 1: + dpad_up = SDL_TRUE; + dpad_right = SDL_TRUE; + break; + case 2: + dpad_right = SDL_TRUE; + break; + case 3: + dpad_right = SDL_TRUE; + dpad_down = SDL_TRUE; + break; + case 4: + dpad_down = SDL_TRUE; + break; + case 5: + dpad_left = SDL_TRUE; + dpad_down = SDL_TRUE; + break; + case 6: + dpad_left = SDL_TRUE; + break; + case 7: + dpad_up = SDL_TRUE; + dpad_left = SDL_TRUE; + break; + default: + break; + } + SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_DOWN, dpad_down); + SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_UP, dpad_up); + SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_RIGHT, dpad_right); + SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_DPAD_LEFT, dpad_left); + } + + if (ctx->last_state[3] != data[3]) { + SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_A, (data[3] & 0x01) ? SDL_PRESSED : SDL_RELEASED); + SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_B, (data[3] & 0x02) ? SDL_PRESSED : SDL_RELEASED); + SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_X, (data[3] & 0x04) ? SDL_PRESSED : SDL_RELEASED); + SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_Y, (data[3] & 0x08) ? SDL_PRESSED : SDL_RELEASED); + SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSHOULDER, (data[3] & 0x10) ? SDL_PRESSED : SDL_RELEASED); + SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, (data[3] & 0x20) ? SDL_PRESSED : SDL_RELEASED); + SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_LEFTSTICK, (data[3] & 0x40) ? SDL_PRESSED : SDL_RELEASED); + SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_RIGHTSTICK, (data[3] & 0x80) ? SDL_PRESSED : SDL_RELEASED); + } + + if (ctx->last_state[4] != data[4]) { + SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_START, (data[4] & 0x01) ? SDL_PRESSED : SDL_RELEASED); + } + + SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTX, SDL_SwapLE16(*(Sint16*)&data[9]) - 0x8000); + SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_LEFTY, SDL_SwapLE16(*(Sint16*)&data[11]) - 0x8000); + + SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTX, SDL_SwapLE16(*(Sint16*)&data[13]) - 0x8000); + SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_RIGHTY, SDL_SwapLE16(*(Sint16*)&data[15]) - 0x8000); + + SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERLEFT, SDL_SwapLE16(*(Sint16*)&data[19]) - 0x8000); + SDL_PrivateJoystickAxis(joystick, SDL_CONTROLLER_AXIS_TRIGGERRIGHT, SDL_SwapLE16(*(Sint16*)&data[21]) - 0x8000); + + if (ctx->last_state[17] != data[17]) { + SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_SHIELD_HOME, (data[17] & 0x01) ? SDL_PRESSED : SDL_RELEASED); + SDL_PrivateJoystickButton(joystick, SDL_CONTROLLER_BUTTON_BACK, (data[17] & 0x02) ? SDL_PRESSED : SDL_RELEASED); + } + + SDL_memcpy(ctx->last_state, data, SDL_min(size, sizeof(ctx->last_state))); +} + +static SDL_bool +HIDAPI_DriverShield_UpdateDevice(SDL_HIDAPI_Device *device) +{ + SDL_DriverShield_Context *ctx = (SDL_DriverShield_Context *)device->context; + SDL_Joystick *joystick = NULL; + Uint8 data[USB_PACKET_LENGTH]; + int size = 0; + + if (device->num_joysticks > 0) { + joystick = SDL_JoystickFromInstanceID(device->joysticks[0]); + } + if (!joystick) { + return SDL_FALSE; + } + + while ((size = SDL_hid_read_timeout(device->dev, data, sizeof(data), 0)) > 0) { +#ifdef DEBUG_SHIELD_PROTOCOL + HIDAPI_DumpPacket("NVIDIA SHIELD packet: size = %d", data, size); +#endif + switch (data[0]) { + case 0x01: + HIDAPI_DriverShield_HandleStatePacket(joystick, ctx, data, size); + break; + case 0x03: + switch (data[1]) { + case CMD_RUMBLE: + ctx->rumble_report_pending = SDL_FALSE; + HIDAPI_DriverShield_SendNextRumble(device); + break; + case CMD_CHARGE_STATE: + ctx->charging = data[2] != 0; + SDL_PrivateJoystickBatteryLevel(joystick, ctx->charging ? SDL_JOYSTICK_POWER_WIRED : ctx->battery_level); + break; + case CMD_BATTERY_STATE: + switch (data[5]) { + case 0: + ctx->battery_level = SDL_JOYSTICK_POWER_EMPTY; + break; + case 1: + ctx->battery_level = SDL_JOYSTICK_POWER_LOW; + break; + case 2: /* 40% */ + case 3: /* 60% */ + case 4: /* 80% */ + ctx->battery_level = SDL_JOYSTICK_POWER_MEDIUM; + break; + case 5: + ctx->battery_level = SDL_JOYSTICK_POWER_FULL; + break; + default: + ctx->battery_level = SDL_JOYSTICK_POWER_UNKNOWN; + break; + } + SDL_PrivateJoystickBatteryLevel(joystick, ctx->charging ? SDL_JOYSTICK_POWER_WIRED : ctx->battery_level); + break; + } + break; + } + } + + /* Ask for battery state again if we're due for an update */ + if (SDL_TICKS_PASSED(SDL_GetTicks(), ctx->last_battery_query_time + BATTERY_POLL_INTERVAL_MS)) { + ctx->last_battery_query_time = SDL_GetTicks(); + HIDAPI_DriverShield_SendCommand(device, CMD_BATTERY_STATE, NULL, 0); + } + + /* Retransmit rumble packets if they've lasted longer than the hardware supports */ + if ((ctx->left_motor_amplitude != 0 || ctx->right_motor_amplitude != 0) && + SDL_TICKS_PASSED(SDL_GetTicks(), ctx->last_rumble_time + RUMBLE_REFRESH_INTERVAL_MS)) { + ctx->rumble_update_pending = SDL_TRUE; + HIDAPI_DriverShield_SendNextRumble(device); + } + + if (size < 0) { + /* Read error, device is disconnected */ + HIDAPI_JoystickDisconnected(device, joystick->instance_id); + } + return (size >= 0); +} + +static void +HIDAPI_DriverShield_CloseJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick) +{ + SDL_LockMutex(device->dev_lock); + { + if (device->dev) { + SDL_hid_close(device->dev); + device->dev = NULL; + } + + SDL_free(device->context); + device->context = NULL; + } + SDL_UnlockMutex(device->dev_lock); +} + +static void +HIDAPI_DriverShield_FreeDevice(SDL_HIDAPI_Device *device) +{ +} + +SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverShield = +{ + SDL_HINT_JOYSTICK_HIDAPI_SHIELD, + SDL_TRUE, + SDL_TRUE, + HIDAPI_DriverShield_IsSupportedDevice, + HIDAPI_DriverShield_GetDeviceName, + HIDAPI_DriverShield_InitDevice, + HIDAPI_DriverShield_GetDevicePlayerIndex, + HIDAPI_DriverShield_SetDevicePlayerIndex, + HIDAPI_DriverShield_UpdateDevice, + HIDAPI_DriverShield_OpenJoystick, + HIDAPI_DriverShield_RumbleJoystick, + HIDAPI_DriverShield_RumbleJoystickTriggers, + HIDAPI_DriverShield_GetJoystickCapabilities, + HIDAPI_DriverShield_SetJoystickLED, + HIDAPI_DriverShield_SendJoystickEffect, + HIDAPI_DriverShield_SetJoystickSensorsEnabled, + HIDAPI_DriverShield_CloseJoystick, + HIDAPI_DriverShield_FreeDevice, +}; + +#endif /* SDL_JOYSTICK_HIDAPI_SHIELD */ + +#endif /* SDL_JOYSTICK_HIDAPI */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/joystick/hidapi/SDL_hidapijoystick.c b/src/joystick/hidapi/SDL_hidapijoystick.c index cf35538d7..112397ab7 100644 --- a/src/joystick/hidapi/SDL_hidapijoystick.c +++ b/src/joystick/hidapi/SDL_hidapijoystick.c @@ -49,6 +49,9 @@ static SDL_HIDAPI_DeviceDriver *SDL_HIDAPI_drivers[] = { #ifdef SDL_JOYSTICK_HIDAPI_LUNA &SDL_HIDAPI_DriverLuna, #endif +#ifdef SDL_JOYSTICK_HIDAPI_SHIELD + &SDL_HIDAPI_DriverShield, +#endif #ifdef SDL_JOYSTICK_HIDAPI_PS4 &SDL_HIDAPI_DriverPS4, #endif diff --git a/src/joystick/hidapi/SDL_hidapijoystick_c.h b/src/joystick/hidapi/SDL_hidapijoystick_c.h index 73c2ad922..6718200d3 100644 --- a/src/joystick/hidapi/SDL_hidapijoystick_c.h +++ b/src/joystick/hidapi/SDL_hidapijoystick_c.h @@ -39,6 +39,7 @@ #define SDL_JOYSTICK_HIDAPI_SWITCH #define SDL_JOYSTICK_HIDAPI_XBOX360 #define SDL_JOYSTICK_HIDAPI_XBOXONE +#define SDL_JOYSTICK_HIDAPI_SHIELD #if defined(__IPHONEOS__) || defined(__TVOS__) || defined(__ANDROID__) /* Very basic Steam Controller support on mobile devices */ @@ -111,6 +112,7 @@ typedef struct _SDL_HIDAPI_DeviceDriver /* HIDAPI device support */ extern SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverGameCube; extern SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverLuna; +extern SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverShield; extern SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverPS4; extern SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverPS5; extern SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverStadia; diff --git a/src/joystick/usb_ids.h b/src/joystick/usb_ids.h index 14c84e5df..0b9102809 100644 --- a/src/joystick/usb_ids.h +++ b/src/joystick/usb_ids.h @@ -39,6 +39,7 @@ #define USB_VENDOR_SONY 0x054c #define USB_VENDOR_VALVE 0x28de +#define USB_PRODUCT_NVIDIA_SHIELD_CONTROLLER 0x7214 #define USB_PRODUCT_AMAZON_LUNA_CONTROLLER 0x0419 #define USB_PRODUCT_GOOGLE_STADIA_CONTROLLER 0x9400 #define USB_PRODUCT_EVORETRO_GAMECUBE_ADAPTER 0x1846