From c528615626d9f7789a5681a946cb3d5bd5d68c2c Mon Sep 17 00:00:00 2001 From: Ethan Lee Date: Tue, 12 Mar 2019 20:27:54 -0400 Subject: [PATCH] hidapi: Add support for Wii U/Switch USB GameCube controller adapter. Note that a single USB device is responsible for all 4 joysticks, so a large rewrite of the DeviceDriver functions was necessary to allow a single device to produce multiple joysticks. --- include/SDL_hints.h | 11 + src/joystick/SDL_gamecontrollerdb.h | 1 + src/joystick/SDL_joystick.c | 11 + src/joystick/SDL_joystick_c.h | 3 + src/joystick/SDL_sysjoystick.h | 3 + src/joystick/controller_type.h | 3 + src/joystick/hidapi/SDL_hidapi_gamecube.c | 339 +++++++++++++++++++++ src/joystick/hidapi/SDL_hidapi_ps4.c | 88 ++++-- src/joystick/hidapi/SDL_hidapi_switch.c | 68 ++++- src/joystick/hidapi/SDL_hidapi_xbox360.c | 80 +++-- src/joystick/hidapi/SDL_hidapi_xboxone.c | 82 +++-- src/joystick/hidapi/SDL_hidapijoystick.c | 179 +++++------ src/joystick/hidapi/SDL_hidapijoystick_c.h | 30 +- 13 files changed, 723 insertions(+), 175 deletions(-) create mode 100644 src/joystick/hidapi/SDL_hidapi_gamecube.c diff --git a/include/SDL_hints.h b/include/SDL_hints.h index cd5adb260465d..ac605db095742 100644 --- a/include/SDL_hints.h +++ b/include/SDL_hints.h @@ -555,6 +555,17 @@ extern "C" { */ #define SDL_HINT_JOYSTICK_HIDAPI_XBOX "SDL_JOYSTICK_HIDAPI_XBOX" +/** + * \brief A variable controlling whether the HIDAPI driver for Nintendo GameCube 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_GAMECUBE "SDL_JOYSTICK_HIDAPI_GAMECUBE" + /** * \brief A variable that controls whether Steam Controllers should be exposed using the SDL joystick and game controller APIs * diff --git a/src/joystick/SDL_gamecontrollerdb.h b/src/joystick/SDL_gamecontrollerdb.h index 7478b9e9893b4..77fe52e3a9ef3 100644 --- a/src/joystick/SDL_gamecontrollerdb.h +++ b/src/joystick/SDL_gamecontrollerdb.h @@ -564,6 +564,7 @@ static const char *s_ControllerMappings [] = "03000000b50700001503000010010000,impact,a:b2,b:b3,back:b8,dpdown:h0.4,dpleft:h0.8,dpright:h0.2,dpup:h0.1,leftshoulder:b4,leftstick:b10,lefttrigger:b5,leftx:a0,lefty:a1,rightshoulder:b6,rightstick:b11,righttrigger:b7,rightx:a3,righty:a2,start:b9,x:b0,y:b1,", "030000009b2800000300000001010000,raphnet.net 4nes4snes v1.5,a:b0,b:b4,back:b2,leftshoulder:b6,leftx:a0,lefty:a1,rightshoulder:b7,start:b3,x:b1,y:b5,", "03000000d80400008200000003000000,IMS PCU#0 Gamepad Interface,platform:Linux,a:b1,b:b0,x:b3,y:b2,back:b4,start:b5,dpup:-a1,dpdown:+a1,dpleft:-a0,dpright:+a0,", + "030000007e0500003703000000016800,Nintendo GameCube Controller,platform:Linux,a:b0,b:b2,x:b1,y:b3,start:b8,rightshoulder:b9,dpup:b7,dpdown:b6,dpleft:b4,dpright:b5,leftx:a0,lefty:a1~,rightx:a2,righty:a3~,lefttrigger:a4,righttrigger:a5,", #endif #if defined(__ANDROID__) "05000000d6020000e5890000dfff3f00,GPD XD Plus,a:b0,b:b1,back:b4,dpdown:b12,dpleft:b13,dpright:b14,dpup:b11,leftshoulder:b9,leftstick:b7,lefttrigger:a3,leftx:a0,lefty:a1,rightshoulder:b10,rightstick:b8,righttrigger:a4,rightx:a2,righty:a5,start:b6,x:b2,y:b3,", diff --git a/src/joystick/SDL_joystick.c b/src/joystick/SDL_joystick.c index a0d5b7b4252eb..a5b3e63b48d51 100644 --- a/src/joystick/SDL_joystick.c +++ b/src/joystick/SDL_joystick.c @@ -1033,6 +1033,11 @@ SDL_JoystickUpdate(void) /* Make sure the list is unlocked while dispatching events to prevent application deadlocks */ SDL_UnlockJoysticks(); + /* Special function for HIDAPI devices, as a single device can provide multiple SDL_Joysticks */ +#ifdef SDL_JOYSTICK_HIDAPI + SDL_HIDAPI_UpdateDevices(); +#endif /* SDL_JOYSTICK_HIDAPI */ + for (joystick = SDL_joysticks; joystick; joystick = joystick->next) { if (joystick->attached) { joystick->driver->Update(joystick); @@ -1187,6 +1192,12 @@ SDL_IsJoystickXboxOne(Uint16 vendor, Uint16 product) return (GuessControllerType(vendor, product) == k_eControllerType_XBoxOneController); } +SDL_bool +SDL_IsJoystickGameCube(Uint16 vendor, Uint16 product) +{ + return (GuessControllerType(vendor, product) == k_eControllerType_GameCube); +} + SDL_bool SDL_IsJoystickXInput(SDL_JoystickGUID guid) { diff --git a/src/joystick/SDL_joystick_c.h b/src/joystick/SDL_joystick_c.h index 165c3700a8210..f02d30b667eec 100644 --- a/src/joystick/SDL_joystick_c.h +++ b/src/joystick/SDL_joystick_c.h @@ -66,6 +66,9 @@ extern SDL_bool SDL_IsJoystickXbox360(Uint16 vendor_id, Uint16 product_id); /* Function to return whether a joystick is an Xbox One controller */ extern SDL_bool SDL_IsJoystickXboxOne(Uint16 vendor_id, Uint16 product_id); +/* Function to return whether a joystick is a GameCube controller */ +extern SDL_bool SDL_IsJoystickGameCube(Uint16 vendor_id, Uint16 product_id); + /* Function to return whether a joystick guid comes from the XInput driver */ extern SDL_bool SDL_IsJoystickXInput(SDL_JoystickGUID guid); diff --git a/src/joystick/SDL_sysjoystick.h b/src/joystick/SDL_sysjoystick.h index 8f575232613de..ef7f082e472cc 100644 --- a/src/joystick/SDL_sysjoystick.h +++ b/src/joystick/SDL_sysjoystick.h @@ -152,6 +152,9 @@ extern SDL_JoystickDriver SDL_IOS_JoystickDriver; extern SDL_JoystickDriver SDL_LINUX_JoystickDriver; extern SDL_JoystickDriver SDL_WINDOWS_JoystickDriver; +/* Special function to update HIDAPI devices */ +extern void SDL_HIDAPI_UpdateDevices(void); + #endif /* SDL_sysjoystick_h_ */ /* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/joystick/controller_type.h b/src/joystick/controller_type.h index 51ac20b4e1eb5..22dec71c171fe 100644 --- a/src/joystick/controller_type.h +++ b/src/joystick/controller_type.h @@ -57,6 +57,7 @@ typedef enum k_eControllerType_SwitchJoyConPair = 41, k_eControllerType_SwitchInputOnlyController = 42, k_eControllerType_MobileTouch = 43, + k_eControllerType_GameCube = 44, k_eControllerType_LastController, // Don't add game controllers below this enumeration - this enumeration can change value // Keyboards and Mice @@ -387,6 +388,8 @@ static const ControllerDescription_t arrControllers[] = { { MAKE_CONTROLLER_ID( 0x20d6, 0xa711 ), k_eControllerType_SwitchInputOnlyController }, // PowerA Wired Controller Plus { MAKE_CONTROLLER_ID( 0x0f0d, 0x0092 ), k_eControllerType_SwitchInputOnlyController }, // HORI Pokken Tournament DX Pro Pad + { MAKE_CONTROLLER_ID( 0x057e, 0x0337 ), k_eControllerType_GameCube }, // Nintendo Wii U/Switch GameCube USB Adapter + // Valve products - don't add to public list { MAKE_CONTROLLER_ID( 0x0000, 0x11fb ), k_eControllerType_MobileTouch }, // Streaming mobile touch virtual controls diff --git a/src/joystick/hidapi/SDL_hidapi_gamecube.c b/src/joystick/hidapi/SDL_hidapi_gamecube.c new file mode 100644 index 0000000000000..d08fff2808cf0 --- /dev/null +++ b/src/joystick/hidapi/SDL_hidapi_gamecube.c @@ -0,0 +1,339 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2019 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_log.h" +#include "SDL_events.h" +#include "SDL_timer.h" +#include "SDL_haptic.h" +#include "SDL_joystick.h" +#include "SDL_gamecontroller.h" +#include "../SDL_sysjoystick.h" +#include "SDL_hidapijoystick_c.h" + + +#ifdef SDL_JOYSTICK_HIDAPI_GAMECUBE + +typedef struct { + SDL_JoystickID joysticks[4]; + Uint8 rumble[5]; + Uint32 rumbleExpiration[4]; + /* Without this variable, hid_write starts to lag a TON */ + Uint8 rumbleUpdate; +} SDL_DriverGameCube_Context; + +static SDL_bool +HIDAPI_DriverGameCube_IsSupportedDevice(Uint16 vendor_id, Uint16 product_id, Uint16 version, int interface_number) +{ + return SDL_IsJoystickGameCube(vendor_id, product_id); +} + +static const char * +HIDAPI_DriverGameCube_GetDeviceName(Uint16 vendor_id, Uint16 product_id) +{ + /* Give a user friendly name for this controller */ + if (SDL_IsJoystickGameCube(vendor_id, product_id)) { + return "Nintendo GameCube Controller"; + } + return NULL; +} + +static SDL_bool +HIDAPI_DriverGameCube_InitDriver(SDL_HIDAPI_DriverData *context, Uint16 vendor_id, Uint16 product_id, int *num_joysticks) +{ + SDL_DriverGameCube_Context *ctx; + Uint8 packet[37]; + Uint8 *curSlot; + Uint8 i; + int size; + Uint8 initMagic = 0x13; + Uint8 rumbleMagic = 0x11; + + ctx = (SDL_DriverGameCube_Context *)SDL_calloc(1, sizeof(*ctx)); + if (!ctx) { + SDL_OutOfMemory(); + return SDL_FALSE; + } + ctx->joysticks[0] = -1; + ctx->joysticks[1] = -1; + ctx->joysticks[2] = -1; + ctx->joysticks[3] = -1; + ctx->rumble[0] = rumbleMagic; + + context->context = ctx; + + /* This is all that's needed to initialize the device. Really! */ + if (hid_write(context->device, &initMagic, sizeof(initMagic)) <= 0) { + SDL_SetError("Couldn't initialize WUP-028"); + SDL_free(ctx); + return SDL_FALSE; + } + + /* Add all the applicable joysticks */ + while ((size = hid_read_timeout(context->device, packet, sizeof(packet), 0)) > 0) { + if (size < 37 || packet[0] != 0x21) { + continue; /* Nothing to do yet...? */ + } + + /* Go through all 4 slots */ + curSlot = packet + 1; + for (i = 0; i < 4; i += 1, curSlot += 9) { + if (curSlot[0] & 0x30) { /* 0x10 - Wired, 0x20 - Wireless */ + if (ctx->joysticks[i] == -1) { + ctx->joysticks[i] = SDL_GetNextJoystickInstanceID(); + + *num_joysticks += 1; + + SDL_PrivateJoystickAdded(ctx->joysticks[i]); + } + } else { + if (ctx->joysticks[i] != -1) { + SDL_PrivateJoystickRemoved(ctx->joysticks[i]); + + *num_joysticks -= 1; + + ctx->joysticks[i] = -1; + } + continue; + } + } + } + + return SDL_TRUE; +} + +static void +HIDAPI_DriverGameCube_QuitDriver(SDL_HIDAPI_DriverData *context, SDL_bool send_event, int *num_joysticks) +{ + SDL_DriverGameCube_Context *ctx = (SDL_DriverGameCube_Context *)context->context; + Uint8 i; + + /* Stop all rumble activity */ + for (i = 1; i < 5; i += 1) { + ctx->rumble[i] = 0; + } + hid_write(context->device, ctx->rumble, sizeof(ctx->rumble)); + + /* Remove all joysticks! */ + for (i = 0; i < 4; i += 1) { + if (ctx->joysticks[i] != -1) { + *num_joysticks -= 1; + if (send_event) { + SDL_PrivateJoystickRemoved(ctx->joysticks[i]); + } + } + } + + SDL_free(context->context); +} + +static SDL_bool +HIDAPI_DriverGameCube_UpdateDriver(SDL_HIDAPI_DriverData *context, int *num_joysticks) +{ + SDL_DriverGameCube_Context *ctx = (SDL_DriverGameCube_Context *)context->context; + SDL_Joystick *joystick; + Uint8 packet[37]; + Uint8 *curSlot; + Uint32 now; + Uint8 i; + int size; + + /* Read input packet */ + while ((size = hid_read_timeout(context->device, packet, sizeof(packet), 0)) > 0) { + if (size < 37 || packet[0] != 0x21) { + continue; /* Nothing to do right now...? */ + } + + /* Go through all 4 slots */ + curSlot = packet + 1; + for (i = 0; i < 4; i += 1, curSlot += 9) { + if (curSlot[0] & 0x30) { /* 0x10 - Wired, 0x20 - Wireless */ + if (ctx->joysticks[i] == -1) { + ctx->joysticks[i] = SDL_GetNextJoystickInstanceID(); + + *num_joysticks += 1; + + SDL_PrivateJoystickAdded(ctx->joysticks[i]); + } + joystick = SDL_JoystickFromInstanceID(ctx->joysticks[i]); + + /* Hasn't been opened yet, skip */ + if (joystick == NULL) { + continue; + } + } else { + if (ctx->joysticks[i] != -1) { + SDL_PrivateJoystickRemoved(ctx->joysticks[i]); + + *num_joysticks -= 1; + + ctx->joysticks[i] = -1; + } + continue; + } + + #define READ_BUTTON(off, flag, button) \ + SDL_PrivateJoystickButton( \ + joystick, \ + button, \ + (curSlot[off] & flag) ? SDL_PRESSED : SDL_RELEASED \ + ); + READ_BUTTON(1, 0x01, 0) /* A */ + READ_BUTTON(1, 0x02, 1) /* B */ + READ_BUTTON(1, 0x04, 2) /* X */ + READ_BUTTON(1, 0x08, 3) /* Y */ + READ_BUTTON(1, 0x10, 4) /* DPAD_LEFT */ + READ_BUTTON(1, 0x20, 5) /* DPAD_RIGHT */ + READ_BUTTON(1, 0x40, 6) /* DPAD_DOWN */ + READ_BUTTON(1, 0x80, 7) /* DPAD_UP */ + READ_BUTTON(2, 0x01, 8) /* START */ + READ_BUTTON(2, 0x02, 9) /* RIGHTSHOULDER */ + /* [2] 0x04 - R, [2] 0x08 - L */ + #undef READ_BUTTON + + /* Axis math taken from SDL_xinputjoystick.c */ + #define READ_AXIS(off, axis) \ + SDL_PrivateJoystickAxis( \ + joystick, \ + axis, \ + (Sint16)(((int)curSlot[off] * 257) - 32768) \ + ); + READ_AXIS(3, 0) /* LEFTX */ + READ_AXIS(4, 1) /* LEFTY */ + READ_AXIS(5, 2) /* RIGHTX */ + READ_AXIS(6, 3) /* RIGHTY */ + READ_AXIS(7, 4) /* TRIGGERLEFT */ + READ_AXIS(8, 5) /* TRIGGERRIGHT */ + #undef READ_AXIS + } + } + + /* Write rumble packet */ + now = SDL_GetTicks(); + for (i = 0; i < 4; i += 1) { + if (ctx->rumbleExpiration[i] > 0) { + if (SDL_TICKS_PASSED(now, ctx->rumbleExpiration[i])) { + ctx->rumble[i + 1] = 0; + ctx->rumbleExpiration[i] = 0; + ctx->rumbleUpdate = 1; + } + } + } + if (ctx->rumbleUpdate) { + hid_write(context->device, ctx->rumble, sizeof(ctx->rumble)); + ctx->rumbleUpdate = 0; + } + + /* If we got here, nothing bad happened! */ + return SDL_TRUE; +} + +static int +HIDAPI_DriverGameCube_NumJoysticks(SDL_HIDAPI_DriverData *context) +{ + SDL_DriverGameCube_Context *ctx = (SDL_DriverGameCube_Context *)context->context; + int i, joysticks = 0; + for (i = 0; i < 4; i += 1) { + if (ctx->joysticks[i] != -1) { + joysticks += 1; + } + } + return joysticks; +} + +static SDL_JoystickID +HIDAPI_DriverGameCube_InstanceIDForIndex(SDL_HIDAPI_DriverData *context, int index) +{ + SDL_DriverGameCube_Context *ctx = (SDL_DriverGameCube_Context *)context->context; + Uint8 i; + for (i = 0; i < 4; i += 1) { + if (ctx->joysticks[i] != -1) { + if (index == 0) { + return ctx->joysticks[i]; + } + index -= 1; + } + } + return -1; /* Should never get here! */ +} + +static SDL_bool +HIDAPI_DriverGameCube_OpenJoystick(SDL_HIDAPI_DriverData *context, SDL_Joystick *joystick) +{ + SDL_DriverGameCube_Context *ctx = (SDL_DriverGameCube_Context *)context->context; + SDL_JoystickID instance = SDL_JoystickInstanceID(joystick); + Uint8 i; + for (i = 0; i < 4; i += 1) { + if (instance == ctx->joysticks[i]) { + joystick->nbuttons = 10; + joystick->naxes = 6; + joystick->epowerlevel = SDL_JOYSTICK_POWER_WIRED; + return SDL_TRUE; + } + } + return SDL_FALSE; /* Should never get here! */ +} + +static int +HIDAPI_DriverGameCube_Rumble(SDL_HIDAPI_DriverData *context, SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms) +{ + SDL_DriverGameCube_Context *ctx = (SDL_DriverGameCube_Context *)context->context; + SDL_JoystickID instance = SDL_JoystickInstanceID(joystick); + Uint8 i, val; + for (i = 0; i < 4; i += 1) { + if (instance == ctx->joysticks[i]) { + val = (low_frequency_rumble > 0 || high_frequency_rumble > 0); + if (val != ctx->rumble[i + 1]) { + ctx->rumble[i + 1] = val; + ctx->rumbleUpdate = 1; + } + if (val && duration_ms < SDL_HAPTIC_INFINITY) { + ctx->rumbleExpiration[i] = SDL_GetTicks() + duration_ms; + } else { + ctx->rumbleExpiration[i] = 0; + } + return 0; + } + } + return -1; +} + +SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverGameCube = +{ + SDL_HINT_JOYSTICK_HIDAPI_GAMECUBE, + SDL_TRUE, + HIDAPI_DriverGameCube_IsSupportedDevice, + HIDAPI_DriverGameCube_GetDeviceName, + HIDAPI_DriverGameCube_InitDriver, + HIDAPI_DriverGameCube_QuitDriver, + HIDAPI_DriverGameCube_UpdateDriver, + HIDAPI_DriverGameCube_NumJoysticks, + HIDAPI_DriverGameCube_InstanceIDForIndex, + HIDAPI_DriverGameCube_OpenJoystick, + HIDAPI_DriverGameCube_Rumble +}; + +#endif /* SDL_JOYSTICK_HIDAPI_GAMECUBE */ + +#endif /* SDL_JOYSTICK_HIDAPI */ diff --git a/src/joystick/hidapi/SDL_hidapi_ps4.c b/src/joystick/hidapi/SDL_hidapi_ps4.c index 9d160f9e70c8f..e6732004e5d0a 100644 --- a/src/joystick/hidapi/SDL_hidapi_ps4.c +++ b/src/joystick/hidapi/SDL_hidapi_ps4.c @@ -108,6 +108,7 @@ typedef struct } DS4EffectsState_t; typedef struct { + SDL_JoystickID joystickID; SDL_bool is_dongle; SDL_bool is_bluetooth; SDL_bool audio_supported; @@ -272,10 +273,8 @@ static SDL_bool HIDAPI_DriverPS4_CanRumble(Uint16 vendor_id, Uint16 product_id) return SDL_TRUE; } -static int HIDAPI_DriverPS4_Rumble(SDL_Joystick *joystick, hid_device *dev, void *context, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms); - static SDL_bool -HIDAPI_DriverPS4_Init(SDL_Joystick *joystick, hid_device *dev, Uint16 vendor_id, Uint16 product_id, void **context) +HIDAPI_DriverPS4_InitDriver(SDL_HIDAPI_DriverData *context, Uint16 vendor_id, Uint16 product_id, int *num_joysticks) { SDL_DriverPS4_Context *ctx; @@ -284,14 +283,14 @@ HIDAPI_DriverPS4_Init(SDL_Joystick *joystick, hid_device *dev, Uint16 vendor_id, SDL_OutOfMemory(); return SDL_FALSE; } - *context = ctx; + context->context = ctx; /* Check for type of connection */ ctx->is_dongle = (vendor_id == SONY_USB_VID && product_id == SONY_DS4_DONGLE_PID); if (ctx->is_dongle) { ctx->is_bluetooth = SDL_FALSE; } else if (vendor_id == SONY_USB_VID) { - ctx->is_bluetooth = !CheckUSBConnected(dev); + ctx->is_bluetooth = !CheckUSBConnected(context->device); } else { /* Third party controllers appear to all be wired */ ctx->is_bluetooth = SDL_FALSE; @@ -314,8 +313,45 @@ HIDAPI_DriverPS4_Init(SDL_Joystick *joystick, hid_device *dev, Uint16 vendor_id, } } + ctx->joystickID = SDL_GetNextJoystickInstanceID(); + *num_joysticks += 1; + SDL_PrivateJoystickAdded(ctx->joystickID); + + return SDL_TRUE; +} + +static void +HIDAPI_DriverPS4_QuitDriver(SDL_HIDAPI_DriverData *context, SDL_bool send_event, int *num_joysticks) +{ + SDL_DriverPS4_Context *ctx = (SDL_DriverPS4_Context *)context->context; + + *num_joysticks -= 1; + if (send_event) { + SDL_PrivateJoystickRemoved(ctx->joystickID); + } + SDL_free(context->context); +} + +static int +HIDAPI_DriverPS4_NumJoysticks(SDL_HIDAPI_DriverData *context) +{ + return 1; +} + +static SDL_JoystickID +HIDAPI_DriverPS4_InstanceIDForIndex(SDL_HIDAPI_DriverData *context, int index) +{ + SDL_DriverPS4_Context *ctx = (SDL_DriverPS4_Context *)context->context; + return ctx->joystickID; +} + +static int HIDAPI_DriverPS4_Rumble(SDL_HIDAPI_DriverData *context, SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms); + +static SDL_bool +HIDAPI_DriverPS4_OpenJoystick(SDL_HIDAPI_DriverData *context, SDL_Joystick *joystick) +{ /* Initialize LED and effect state */ - HIDAPI_DriverPS4_Rumble(joystick, dev, ctx, 0, 0, 0); + HIDAPI_DriverPS4_Rumble(context, joystick, 0, 0, 0); /* Initialize the joystick capabilities */ joystick->nbuttons = SDL_CONTROLLER_BUTTON_MAX; @@ -326,9 +362,9 @@ HIDAPI_DriverPS4_Init(SDL_Joystick *joystick, hid_device *dev, Uint16 vendor_id, } static int -HIDAPI_DriverPS4_Rumble(SDL_Joystick *joystick, hid_device *dev, void *context, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms) +HIDAPI_DriverPS4_Rumble(SDL_HIDAPI_DriverData *context, SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms) { - SDL_DriverPS4_Context *ctx = (SDL_DriverPS4_Context *)context; + SDL_DriverPS4_Context *ctx = (SDL_DriverPS4_Context *)context->context; DS4EffectsState_t *effects; Uint8 data[78]; int report_size, offset; @@ -386,7 +422,7 @@ HIDAPI_DriverPS4_Rumble(SDL_Joystick *joystick, hid_device *dev, void *context, SDL_memcpy(&data[report_size - sizeof(unCRC)], &unCRC, sizeof(unCRC)); } - if (hid_write(dev, data, report_size) != report_size) { + if (hid_write(context->device, data, report_size) != report_size) { return SDL_SetError("Couldn't send rumble packet"); } @@ -508,20 +544,25 @@ HIDAPI_DriverPS4_HandleStatePacket(SDL_Joystick *joystick, hid_device *dev, SDL_ } static SDL_bool -HIDAPI_DriverPS4_Update(SDL_Joystick *joystick, hid_device *dev, void *context) +HIDAPI_DriverPS4_UpdateDriver(SDL_HIDAPI_DriverData *context, int *num_joysticks) { - SDL_DriverPS4_Context *ctx = (SDL_DriverPS4_Context *)context; + SDL_DriverPS4_Context *ctx = (SDL_DriverPS4_Context *)context->context; + SDL_Joystick *joystick = SDL_JoystickFromInstanceID(ctx->joystickID); Uint8 data[USB_PACKET_LENGTH]; int size; - while ((size = hid_read_timeout(dev, data, sizeof(data), 0)) > 0) { + if (joystick == NULL) { + return SDL_TRUE; /* Nothing to do right now! */ + } + + while ((size = hid_read_timeout(context->device, data, sizeof(data), 0)) > 0) { switch (data[0]) { case k_EPS4ReportIdUsbState: - HIDAPI_DriverPS4_HandleStatePacket(joystick, dev, ctx, (PS4StatePacket_t *)&data[1]); + HIDAPI_DriverPS4_HandleStatePacket(joystick, context->device, ctx, (PS4StatePacket_t *)&data[1]); break; case k_EPS4ReportIdBluetoothState: /* Bluetooth state packets have two additional bytes at the beginning */ - HIDAPI_DriverPS4_HandleStatePacket(joystick, dev, ctx, (PS4StatePacket_t *)&data[3]); + HIDAPI_DriverPS4_HandleStatePacket(joystick, context->device, ctx, (PS4StatePacket_t *)&data[3]); break; default: #ifdef DEBUG_JOYSTICK @@ -534,29 +575,26 @@ HIDAPI_DriverPS4_Update(SDL_Joystick *joystick, hid_device *dev, void *context) if (ctx->rumble_expiration) { Uint32 now = SDL_GetTicks(); if (SDL_TICKS_PASSED(now, ctx->rumble_expiration)) { - HIDAPI_DriverPS4_Rumble(joystick, dev, context, 0, 0, 0); + HIDAPI_DriverPS4_Rumble(context, joystick, 0, 0, 0); } } return (size >= 0); } -static void -HIDAPI_DriverPS4_Quit(SDL_Joystick *joystick, hid_device *dev, void *context) -{ - SDL_free(context); -} - SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverPS4 = { SDL_HINT_JOYSTICK_HIDAPI_PS4, SDL_TRUE, HIDAPI_DriverPS4_IsSupportedDevice, HIDAPI_DriverPS4_GetDeviceName, - HIDAPI_DriverPS4_Init, - HIDAPI_DriverPS4_Rumble, - HIDAPI_DriverPS4_Update, - HIDAPI_DriverPS4_Quit + HIDAPI_DriverPS4_InitDriver, + HIDAPI_DriverPS4_QuitDriver, + HIDAPI_DriverPS4_UpdateDriver, + HIDAPI_DriverPS4_NumJoysticks, + HIDAPI_DriverPS4_InstanceIDForIndex, + HIDAPI_DriverPS4_OpenJoystick, + HIDAPI_DriverPS4_Rumble }; #endif /* SDL_JOYSTICK_HIDAPI_PS4 */ diff --git a/src/joystick/hidapi/SDL_hidapi_switch.c b/src/joystick/hidapi/SDL_hidapi_switch.c index 27c988c9de035..b81112ba7eead 100644 --- a/src/joystick/hidapi/SDL_hidapi_switch.c +++ b/src/joystick/hidapi/SDL_hidapi_switch.c @@ -183,6 +183,7 @@ typedef struct #pragma pack() typedef struct { + SDL_JoystickID joystickID; hid_device *dev; SDL_bool m_bIsUsingBluetooth; Uint8 m_nCommandNumber; @@ -570,7 +571,7 @@ static Sint16 ApplyStickCalibration(SDL_DriverSwitch_Context *ctx, int nStick, i } static SDL_bool -HIDAPI_DriverSwitch_Init(SDL_Joystick *joystick, hid_device *dev, Uint16 vendor_id, Uint16 product_id, void **context) +HIDAPI_DriverSwitch_InitDriver(SDL_HIDAPI_DriverData *context, Uint16 vendor_id, Uint16 product_id, int *num_joysticks) { SDL_DriverSwitch_Context *ctx; Uint8 input_mode; @@ -580,9 +581,9 @@ HIDAPI_DriverSwitch_Init(SDL_Joystick *joystick, hid_device *dev, Uint16 vendor_ SDL_OutOfMemory(); return SDL_FALSE; } - ctx->dev = dev; + ctx->dev = context->device; - *context = ctx; + context->context = ctx; /* Initialize rumble data */ SetNeutralRumble(&ctx->m_RumblePacket.rumbleData[0]); @@ -627,6 +628,18 @@ HIDAPI_DriverSwitch_Init(SDL_Joystick *joystick, hid_device *dev, Uint16 vendor_ } } + ctx->joystickID = SDL_GetNextJoystickInstanceID(); + *num_joysticks += 1; + SDL_PrivateJoystickAdded(ctx->joystickID); + + return SDL_TRUE; +} + +static SDL_bool +HIDAPI_DriverSwitch_OpenJoystick(SDL_HIDAPI_DriverData *context, SDL_Joystick *joystick) +{ + SDL_DriverSwitch_Context *ctx = (SDL_DriverSwitch_Context *)context->context; + /* Set the LED state */ SetHomeLED(ctx, 100); SetSlotLED(ctx, (joystick->instance_id % 4)); @@ -640,9 +653,9 @@ HIDAPI_DriverSwitch_Init(SDL_Joystick *joystick, hid_device *dev, Uint16 vendor_ } static int -HIDAPI_DriverSwitch_Rumble(SDL_Joystick *joystick, hid_device *dev, void *context, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms) +HIDAPI_DriverSwitch_Rumble(SDL_HIDAPI_DriverData *context, SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms) { - SDL_DriverSwitch_Context *ctx = (SDL_DriverSwitch_Context *)context; + SDL_DriverSwitch_Context *ctx = (SDL_DriverSwitch_Context *)context->context; /* Experimentally determined rumble values. These will only matter on some controllers as tested ones * seem to disregard these and just use any non-zero rumble values as a binary flag for constant rumble @@ -847,11 +860,16 @@ static void HandleFullControllerState(SDL_Joystick *joystick, SDL_DriverSwitch_C } static SDL_bool -HIDAPI_DriverSwitch_Update(SDL_Joystick *joystick, hid_device *dev, void *context) +HIDAPI_DriverSwitch_UpdateDriver(SDL_HIDAPI_DriverData *context, int *num_joysticks) { - SDL_DriverSwitch_Context *ctx = (SDL_DriverSwitch_Context *)context; + SDL_DriverSwitch_Context *ctx = (SDL_DriverSwitch_Context *)context->context; + SDL_Joystick *joystick = SDL_JoystickFromInstanceID(ctx->joystickID); int size; + if (joystick == NULL) { + return SDL_TRUE; /* Nothing to do right now! */ + } + while ((size = ReadInput(ctx)) > 0) { switch (ctx->m_rgucReadBuffer[0]) { case k_eSwitchInputReportIDs_SimpleControllerState: @@ -868,7 +886,7 @@ HIDAPI_DriverSwitch_Update(SDL_Joystick *joystick, hid_device *dev, void *contex if (ctx->m_nRumbleExpiration) { Uint32 now = SDL_GetTicks(); if (SDL_TICKS_PASSED(now, ctx->m_nRumbleExpiration)) { - HIDAPI_DriverSwitch_Rumble(joystick, dev, context, 0, 0, 0); + HIDAPI_DriverSwitch_Rumble(context, joystick, 0, 0, 0); } } @@ -876,14 +894,31 @@ HIDAPI_DriverSwitch_Update(SDL_Joystick *joystick, hid_device *dev, void *contex } static void -HIDAPI_DriverSwitch_Quit(SDL_Joystick *joystick, hid_device *dev, void *context) +HIDAPI_DriverSwitch_QuitDriver(SDL_HIDAPI_DriverData *context, SDL_bool send_event, int *num_joysticks) { - SDL_DriverSwitch_Context *ctx = (SDL_DriverSwitch_Context *)context; + SDL_DriverSwitch_Context *ctx = (SDL_DriverSwitch_Context *)context->context; /* Restore simple input mode for other applications */ SetInputMode(ctx, k_eSwitchInputReportIDs_SimpleControllerState); - SDL_free(context); + *num_joysticks -= 1; + if (send_event) { + SDL_PrivateJoystickRemoved(ctx->joystickID); + } + SDL_free(context->context); +} + +static int +HIDAPI_DriverSwitch_NumJoysticks(SDL_HIDAPI_DriverData *context) +{ + return 1; +} + +static SDL_JoystickID +HIDAPI_DriverSwitch_InstanceIDForIndex(SDL_HIDAPI_DriverData *context, int index) +{ + SDL_DriverSwitch_Context *ctx = (SDL_DriverSwitch_Context *)context->context; + return ctx->joystickID; } SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverSwitch = @@ -892,10 +927,13 @@ SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverSwitch = SDL_TRUE, HIDAPI_DriverSwitch_IsSupportedDevice, HIDAPI_DriverSwitch_GetDeviceName, - HIDAPI_DriverSwitch_Init, - HIDAPI_DriverSwitch_Rumble, - HIDAPI_DriverSwitch_Update, - HIDAPI_DriverSwitch_Quit + HIDAPI_DriverSwitch_InitDriver, + HIDAPI_DriverSwitch_QuitDriver, + HIDAPI_DriverSwitch_UpdateDriver, + HIDAPI_DriverSwitch_NumJoysticks, + HIDAPI_DriverSwitch_InstanceIDForIndex, + HIDAPI_DriverSwitch_OpenJoystick, + HIDAPI_DriverSwitch_Rumble }; #endif /* SDL_JOYSTICK_HIDAPI_SWITCH */ diff --git a/src/joystick/hidapi/SDL_hidapi_xbox360.c b/src/joystick/hidapi/SDL_hidapi_xbox360.c index ab1ee9e969ee2..ad0a1922db267 100644 --- a/src/joystick/hidapi/SDL_hidapi_xbox360.c +++ b/src/joystick/hidapi/SDL_hidapi_xbox360.c @@ -54,6 +54,7 @@ typedef struct { + SDL_JoystickID joystickID; Uint8 last_state[USB_PACKET_LENGTH]; Uint32 rumble_expiration; #ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT @@ -277,7 +278,7 @@ static SDL_bool SetSlotLED(hid_device *dev, Uint8 slot) } static SDL_bool -HIDAPI_DriverXbox360_Init(SDL_Joystick *joystick, hid_device *dev, Uint16 vendor_id, Uint16 product_id, void **context) +HIDAPI_DriverXbox360_InitDriver(SDL_HIDAPI_DriverData *context, Uint16 vendor_id, Uint16 product_id, int *num_joysticks) { SDL_DriverXbox360_Context *ctx; @@ -296,10 +297,20 @@ HIDAPI_DriverXbox360_Init(SDL_Joystick *joystick, hid_device *dev, Uint16 vendor #ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT HIDAPI_DriverXbox360_InitWindowsGamingInput(ctx); #endif - *context = ctx; + context->context = ctx; + ctx->joystickID = SDL_GetNextJoystickInstanceID(); + *num_joysticks += 1; + SDL_PrivateJoystickAdded(ctx->joystickID); + + return SDL_TRUE; +} + +static SDL_bool +HIDAPI_DriverXbox360_OpenJoystick(SDL_HIDAPI_DriverData *context, SDL_Joystick *joystick) +{ /* Set the controller LED */ - SetSlotLED(dev, (joystick->instance_id % 4)); + SetSlotLED(context->device, (joystick->instance_id % 4)); /* Initialize the joystick capabilities */ joystick->nbuttons = SDL_CONTROLLER_BUTTON_MAX; @@ -310,9 +321,22 @@ HIDAPI_DriverXbox360_Init(SDL_Joystick *joystick, hid_device *dev, Uint16 vendor } static int -HIDAPI_DriverXbox360_Rumble(SDL_Joystick *joystick, hid_device *dev, void *context, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms) +HIDAPI_DriverXbox360_NumJoysticks(SDL_HIDAPI_DriverData *context) { - SDL_DriverXbox360_Context *ctx = (SDL_DriverXbox360_Context *)context; + return 1; +} + +static SDL_JoystickID +HIDAPI_DriverXbox360_InstanceIDForIndex(SDL_HIDAPI_DriverData *context, int index) +{ + SDL_DriverXbox360_Context *ctx = (SDL_DriverXbox360_Context *)context->context; + return ctx->joystickID; +} + +static int +HIDAPI_DriverXbox360_Rumble(SDL_HIDAPI_DriverData *context, SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms) +{ + SDL_DriverXbox360_Context *ctx = (SDL_DriverXbox360_Context *)context->context; #ifdef __WIN32__ SDL_bool rumbled = SDL_FALSE; @@ -365,7 +389,7 @@ HIDAPI_DriverXbox360_Rumble(SDL_Joystick *joystick, hid_device *dev, void *conte rumble_packet[4] = (high_frequency_rumble >> 8); #endif - if (hid_write(dev, rumble_packet, sizeof(rumble_packet)) != sizeof(rumble_packet)) { + if (hid_write(context->device, rumble_packet, sizeof(rumble_packet)) != sizeof(rumble_packet)) { return SDL_SetError("Couldn't send rumble packet"); } #endif /* __WIN32__ */ @@ -705,26 +729,31 @@ HIDAPI_DriverXboxOneS_HandleGuidePacket(SDL_Joystick *joystick, hid_device *dev, #endif /* __MACOSX__ */ static SDL_bool -HIDAPI_DriverXbox360_Update(SDL_Joystick *joystick, hid_device *dev, void *context) +HIDAPI_DriverXbox360_UpdateDriver(SDL_HIDAPI_DriverData *context, int *num_joysticks) { - SDL_DriverXbox360_Context *ctx = (SDL_DriverXbox360_Context *)context; + SDL_DriverXbox360_Context *ctx = (SDL_DriverXbox360_Context *)context->context; + SDL_Joystick *joystick = SDL_JoystickFromInstanceID(ctx->joystickID); Uint8 data[USB_PACKET_LENGTH]; int size; - while ((size = hid_read_timeout(dev, data, sizeof(data), 0)) > 0) { + if (joystick == NULL) { + return SDL_TRUE; /* Nothing to do right now! */ + } + + while ((size = hid_read_timeout(context->device, data, sizeof(data), 0)) > 0) { #ifdef __WIN32__ - HIDAPI_DriverXbox360_HandleStatePacket(joystick, dev, ctx, data, size); + HIDAPI_DriverXbox360_HandleStatePacket(joystick, context->device, ctx, data, size); #else switch (data[0]) { case 0x00: - HIDAPI_DriverXbox360_HandleStatePacket(joystick, dev, ctx, data, size); + HIDAPI_DriverXbox360_HandleStatePacket(joystick, context->device, ctx, data, size); break; #ifdef __MACOSX__ case 0x01: - HIDAPI_DriverXboxOneS_HandleStatePacket(joystick, dev, ctx, data, size); + HIDAPI_DriverXboxOneS_HandleStatePacket(joystick, context->device, ctx, data, size); break; case 0x02: - HIDAPI_DriverXboxOneS_HandleGuidePacket(joystick, dev, ctx, data, size); + HIDAPI_DriverXboxOneS_HandleGuidePacket(joystick, context->device, ctx, data, size); break; #endif default: @@ -742,7 +771,7 @@ HIDAPI_DriverXbox360_Update(SDL_Joystick *joystick, hid_device *dev, void *conte if (ctx->rumble_expiration) { Uint32 now = SDL_GetTicks(); if (SDL_TICKS_PASSED(now, ctx->rumble_expiration)) { - HIDAPI_DriverXbox360_Rumble(joystick, dev, context, 0, 0, 0); + HIDAPI_DriverXbox360_Rumble(context, joystick, 0, 0, 0); } } @@ -750,11 +779,9 @@ HIDAPI_DriverXbox360_Update(SDL_Joystick *joystick, hid_device *dev, void *conte } static void -HIDAPI_DriverXbox360_Quit(SDL_Joystick *joystick, hid_device *dev, void *context) +HIDAPI_DriverXbox360_QuitDriver(SDL_HIDAPI_DriverData *context, SDL_bool send_event, int *num_joysticks) { -#if defined(SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT) || defined(SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT) - SDL_DriverXbox360_Context *ctx = (SDL_DriverXbox360_Context *)context; -#endif + SDL_DriverXbox360_Context *ctx = (SDL_DriverXbox360_Context *)context->context; #ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT if (ctx->xinput_enabled) { @@ -765,7 +792,11 @@ HIDAPI_DriverXbox360_Quit(SDL_Joystick *joystick, hid_device *dev, void *context #ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT HIDAPI_DriverXbox360_InitWindowsGamingInput(ctx); #endif - SDL_free(context); + *num_joysticks -= 1; + if (send_event) { + SDL_PrivateJoystickRemoved(ctx->joystickID); + } + SDL_free(context->context); } SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverXbox360 = @@ -774,10 +805,13 @@ SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverXbox360 = SDL_TRUE, HIDAPI_DriverXbox360_IsSupportedDevice, HIDAPI_DriverXbox360_GetDeviceName, - HIDAPI_DriverXbox360_Init, - HIDAPI_DriverXbox360_Rumble, - HIDAPI_DriverXbox360_Update, - HIDAPI_DriverXbox360_Quit + HIDAPI_DriverXbox360_InitDriver, + HIDAPI_DriverXbox360_QuitDriver, + HIDAPI_DriverXbox360_UpdateDriver, + HIDAPI_DriverXbox360_NumJoysticks, + HIDAPI_DriverXbox360_InstanceIDForIndex, + HIDAPI_DriverXbox360_OpenJoystick, + HIDAPI_DriverXbox360_Rumble }; #endif /* SDL_JOYSTICK_HIDAPI_XBOX360 */ diff --git a/src/joystick/hidapi/SDL_hidapi_xboxone.c b/src/joystick/hidapi/SDL_hidapi_xboxone.c index fde74bb237500..90c2aa3bf13f5 100644 --- a/src/joystick/hidapi/SDL_hidapi_xboxone.c +++ b/src/joystick/hidapi/SDL_hidapi_xboxone.c @@ -124,6 +124,7 @@ static const SDL_DriverXboxOne_InitPacket xboxone_init_packets[] = { }; typedef struct { + SDL_JoystickID joystickID; Uint8 sequence; Uint8 last_state[USB_PACKET_LENGTH]; Uint32 rumble_expiration; @@ -143,7 +144,7 @@ HIDAPI_DriverXboxOne_GetDeviceName(Uint16 vendor_id, Uint16 product_id) } static SDL_bool -HIDAPI_DriverXboxOne_Init(SDL_Joystick *joystick, hid_device *dev, Uint16 vendor_id, Uint16 product_id, void **context) +HIDAPI_DriverXboxOne_InitDriver(SDL_HIDAPI_DriverData *context, Uint16 vendor_id, Uint16 product_id, int *num_joysticks) { SDL_DriverXboxOne_Context *ctx; int i; @@ -154,7 +155,7 @@ HIDAPI_DriverXboxOne_Init(SDL_Joystick *joystick, hid_device *dev, Uint16 vendor SDL_OutOfMemory(); return SDL_FALSE; } - *context = ctx; + context->context = ctx; /* Send the controller init data */ for (i = 0; i < SDL_arraysize(xboxone_init_packets); ++i) { @@ -162,7 +163,7 @@ HIDAPI_DriverXboxOne_Init(SDL_Joystick *joystick, hid_device *dev, Uint16 vendor if (!packet->vendor_id || (vendor_id == packet->vendor_id && product_id == packet->product_id)) { SDL_memcpy(init_packet, packet->data, packet->size); init_packet[2] = ctx->sequence++; - if (hid_write(dev, init_packet, packet->size) != packet->size) { + if (hid_write(context->device, init_packet, packet->size) != packet->size) { SDL_SetError("Couldn't write Xbox One initialization packet"); SDL_free(ctx); return SDL_FALSE; @@ -170,6 +171,16 @@ HIDAPI_DriverXboxOne_Init(SDL_Joystick *joystick, hid_device *dev, Uint16 vendor } } + ctx->joystickID = SDL_GetNextJoystickInstanceID(); + *num_joysticks += 1; + SDL_PrivateJoystickAdded(ctx->joystickID); + + return SDL_TRUE; +} + +static SDL_bool +HIDAPI_DriverXboxOne_OpenJoystick(SDL_HIDAPI_DriverData *context, SDL_Joystick *joystick) +{ /* Initialize the joystick capabilities */ joystick->nbuttons = SDL_CONTROLLER_BUTTON_MAX; joystick->naxes = SDL_CONTROLLER_AXIS_MAX; @@ -178,10 +189,35 @@ HIDAPI_DriverXboxOne_Init(SDL_Joystick *joystick, hid_device *dev, Uint16 vendor return SDL_TRUE; } +static void +HIDAPI_DriverXboxOne_QuitDriver(SDL_HIDAPI_DriverData *context, SDL_bool send_event, int *num_joysticks) +{ + SDL_DriverXboxOne_Context *ctx = (SDL_DriverXboxOne_Context *)context->context; + + *num_joysticks -= 1; + if (send_event) { + SDL_PrivateJoystickRemoved(ctx->joystickID); + } + SDL_free(context->context); +} + static int -HIDAPI_DriverXboxOne_Rumble(SDL_Joystick *joystick, hid_device *dev, void *context, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms) +HIDAPI_DriverXboxOne_NumJoysticks(SDL_HIDAPI_DriverData *context) { - SDL_DriverXboxOne_Context *ctx = (SDL_DriverXboxOne_Context *)context; + return 1; +} + +static SDL_JoystickID +HIDAPI_DriverXboxOne_InstanceIDForIndex(SDL_HIDAPI_DriverData *context, int index) +{ + SDL_DriverXboxOne_Context *ctx = (SDL_DriverXboxOne_Context *)context->context; + return ctx->joystickID; +} + +static int +HIDAPI_DriverXboxOne_Rumble(SDL_HIDAPI_DriverData *context, SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms) +{ + SDL_DriverXboxOne_Context *ctx = (SDL_DriverXboxOne_Context *)context->context; Uint8 rumble_packet[] = { 0x09, 0x00, 0x00, 0x09, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF }; /* The Rock Candy Xbox One Controller limits the range of @@ -194,7 +230,7 @@ HIDAPI_DriverXboxOne_Rumble(SDL_Joystick *joystick, hid_device *dev, void *conte rumble_packet[8] = (low_frequency_rumble >> 9); rumble_packet[9] = (high_frequency_rumble >> 9); - if (hid_write(dev, rumble_packet, sizeof(rumble_packet)) != sizeof(rumble_packet)) { + if (hid_write(context->device, rumble_packet, sizeof(rumble_packet)) != sizeof(rumble_packet)) { return SDL_SetError("Couldn't send rumble packet"); } @@ -267,19 +303,24 @@ HIDAPI_DriverXboxOne_HandleModePacket(SDL_Joystick *joystick, hid_device *dev, S } static SDL_bool -HIDAPI_DriverXboxOne_Update(SDL_Joystick *joystick, hid_device *dev, void *context) +HIDAPI_DriverXboxOne_UpdateDriver(SDL_HIDAPI_DriverData *context, int *num_joysticks) { - SDL_DriverXboxOne_Context *ctx = (SDL_DriverXboxOne_Context *)context; + SDL_DriverXboxOne_Context *ctx = (SDL_DriverXboxOne_Context *)context->context; + SDL_Joystick *joystick = SDL_JoystickFromInstanceID(ctx->joystickID); Uint8 data[USB_PACKET_LENGTH]; int size; - while ((size = hid_read_timeout(dev, data, sizeof(data), 0)) > 0) { + if (joystick == NULL) { + return SDL_TRUE; /* Nothing to do right now! */ + } + + while ((size = hid_read_timeout(context->device, data, sizeof(data), 0)) > 0) { switch (data[0]) { case 0x20: - HIDAPI_DriverXboxOne_HandleStatePacket(joystick, dev, ctx, data, size); + HIDAPI_DriverXboxOne_HandleStatePacket(joystick, context->device, ctx, data, size); break; case 0x07: - HIDAPI_DriverXboxOne_HandleModePacket(joystick, dev, ctx, data, size); + HIDAPI_DriverXboxOne_HandleModePacket(joystick, context->device, ctx, data, size); break; default: #ifdef DEBUG_JOYSTICK @@ -292,29 +333,26 @@ HIDAPI_DriverXboxOne_Update(SDL_Joystick *joystick, hid_device *dev, void *conte if (ctx->rumble_expiration) { Uint32 now = SDL_GetTicks(); if (SDL_TICKS_PASSED(now, ctx->rumble_expiration)) { - HIDAPI_DriverXboxOne_Rumble(joystick, dev, context, 0, 0, 0); + HIDAPI_DriverXboxOne_Rumble(context, joystick, 0, 0, 0); } } return (size >= 0); } -static void -HIDAPI_DriverXboxOne_Quit(SDL_Joystick *joystick, hid_device *dev, void *context) -{ - SDL_free(context); -} - SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverXboxOne = { SDL_HINT_JOYSTICK_HIDAPI_XBOX, SDL_TRUE, HIDAPI_DriverXboxOne_IsSupportedDevice, HIDAPI_DriverXboxOne_GetDeviceName, - HIDAPI_DriverXboxOne_Init, - HIDAPI_DriverXboxOne_Rumble, - HIDAPI_DriverXboxOne_Update, - HIDAPI_DriverXboxOne_Quit + HIDAPI_DriverXboxOne_InitDriver, + HIDAPI_DriverXboxOne_QuitDriver, + HIDAPI_DriverXboxOne_UpdateDriver, + HIDAPI_DriverXboxOne_NumJoysticks, + HIDAPI_DriverXboxOne_InstanceIDForIndex, + HIDAPI_DriverXboxOne_OpenJoystick, + HIDAPI_DriverXboxOne_Rumble }; #endif /* SDL_JOYSTICK_HIDAPI_XBOXONE */ diff --git a/src/joystick/hidapi/SDL_hidapijoystick.c b/src/joystick/hidapi/SDL_hidapijoystick.c index e4203544c7c3b..7cbd9f44e2948 100644 --- a/src/joystick/hidapi/SDL_hidapijoystick.c +++ b/src/joystick/hidapi/SDL_hidapijoystick.c @@ -50,18 +50,10 @@ #endif #endif -struct joystick_hwdata -{ - SDL_HIDAPI_DeviceDriver *driver; - void *context; - - SDL_mutex *mutex; - hid_device *dev; -}; - typedef struct _SDL_HIDAPI_Device { - SDL_JoystickID instance_id; + SDL_HIDAPI_DriverData devdata; + SDL_mutex *mutex; char *name; char *path; Uint16 vendor_id; @@ -95,6 +87,9 @@ static SDL_HIDAPI_DeviceDriver *SDL_HIDAPI_drivers[] = { #ifdef SDL_JOYSTICK_HIDAPI_XBOXONE &SDL_HIDAPI_DriverXboxOne, #endif +#ifdef SDL_JOYSTICK_HIDAPI_GAMECUBE + &SDL_HIDAPI_DriverGameCube, +#endif }; static SDL_HIDAPI_Device *SDL_HIDAPI_devices; static int SDL_HIDAPI_numjoysticks = 0; @@ -393,6 +388,36 @@ HIDAPI_ShutdownDiscovery() #endif } +static void +HIDAPI_InitDriver(SDL_HIDAPI_Device *device) +{ + device->devdata.device = hid_open_path(device->path, 0); + if (!device->devdata.device) { + SDL_SetError("Couldn't open HID device %s", device->path); + device->driver = NULL; + } else { + device->driver->InitDriver( + &device->devdata, + device->vendor_id, + device->product_id, + &SDL_HIDAPI_numjoysticks + ); + device->mutex = SDL_CreateMutex(); + } +} + +static void +HIDAPI_QuitDriver(SDL_HIDAPI_Device *device, SDL_bool send_event) +{ + device->driver->QuitDriver( + &device->devdata, + send_event, + &SDL_HIDAPI_numjoysticks + ); + hid_close(device->devdata.device); + SDL_DestroyMutex(device->mutex); + device->driver = NULL; +} const char * HIDAPI_XboxControllerName(Uint16 vendor_id, Uint16 product_id) @@ -605,15 +630,17 @@ HIDAPI_GetDeviceDriver(SDL_HIDAPI_Device *device) } static SDL_HIDAPI_Device * -HIDAPI_GetJoystickByIndex(int device_index) +HIDAPI_GetDeviceByIndex(int device_index) { SDL_HIDAPI_Device *device = SDL_HIDAPI_devices; + int joysticks; while (device) { if (device->driver) { - if (device_index == 0) { + joysticks = device->driver->NumJoysticks(&device->devdata); + if (device_index < joysticks) { break; } - --device_index; + device_index -= joysticks; } device = device->next; } @@ -660,20 +687,12 @@ SDL_HIDAPIDriverHintChanged(void *userdata, const char *name, const char *oldVal while (device) { if (device->driver) { if (!device->driver->enabled) { - device->driver = NULL; - - --SDL_HIDAPI_numjoysticks; - - SDL_PrivateJoystickRemoved(device->instance_id); + HIDAPI_QuitDriver(device, SDL_TRUE); } } else { device->driver = HIDAPI_GetDeviceDriver(device); if (device->driver) { - device->instance_id = SDL_GetNextJoystickInstanceID(); - - ++SDL_HIDAPI_numjoysticks; - - SDL_PrivateJoystickAdded(device->instance_id); + HIDAPI_InitDriver(device); } } device = device->next; @@ -723,7 +742,6 @@ HIDAPI_AddDevice(struct hid_device_info *info) if (!device) { return; } - device->instance_id = -1; device->seen = SDL_TRUE; device->vendor_id = info->vendor_id; device->product_id = info->product_id; @@ -818,12 +836,8 @@ HIDAPI_AddDevice(struct hid_device_info *info) } if (device->driver) { - /* It's a joystick! */ - device->instance_id = SDL_GetNextJoystickInstanceID(); - - ++SDL_HIDAPI_numjoysticks; - - SDL_PrivateJoystickAdded(device->instance_id); + /* It's a joystick device! */ + HIDAPI_InitDriver(device); } } @@ -840,11 +854,8 @@ HIDAPI_DelDevice(SDL_HIDAPI_Device *device, SDL_bool send_event) SDL_HIDAPI_devices = curr->next; } - if (device->driver && send_event) { - /* Need to decrement the joystick count before we post the event */ - --SDL_HIDAPI_numjoysticks; - - SDL_PrivateJoystickRemoved(device->instance_id); + if (device->driver) { + HIDAPI_QuitDriver(device, send_event); } SDL_free(device->name); @@ -931,7 +942,7 @@ HIDAPI_JoystickDetect(void) static const char * HIDAPI_JoystickGetDeviceName(int device_index) { - return HIDAPI_GetJoystickByIndex(device_index)->name; + return HIDAPI_GetDeviceByIndex(device_index)->name; } static int @@ -943,89 +954,61 @@ HIDAPI_JoystickGetDevicePlayerIndex(int device_index) static SDL_JoystickGUID HIDAPI_JoystickGetDeviceGUID(int device_index) { - return HIDAPI_GetJoystickByIndex(device_index)->guid; + return HIDAPI_GetDeviceByIndex(device_index)->guid; } static SDL_JoystickID HIDAPI_JoystickGetDeviceInstanceID(int device_index) { - return HIDAPI_GetJoystickByIndex(device_index)->instance_id; + SDL_HIDAPI_Device *device = SDL_HIDAPI_devices; + int joysticks; + while (device) { + if (device->driver) { + joysticks = device->driver->NumJoysticks(&device->devdata); + if (device_index < joysticks) { + break; + } + device_index -= joysticks; + } + device = device->next; + } + return device->driver->InstanceIDForIndex(&device->devdata, device_index); } static int HIDAPI_JoystickOpen(SDL_Joystick * joystick, int device_index) { - SDL_HIDAPI_Device *device = HIDAPI_GetJoystickByIndex(device_index); - struct joystick_hwdata *hwdata; - - hwdata = (struct joystick_hwdata *)SDL_calloc(1, sizeof(*hwdata)); - if (!hwdata) { - return SDL_OutOfMemory(); - } - - hwdata->driver = device->driver; - hwdata->dev = hid_open_path(device->path, 0); - if (!hwdata->dev) { - SDL_free(hwdata); - return SDL_SetError("Couldn't open HID device %s", device->path); - } - hwdata->mutex = SDL_CreateMutex(); + SDL_HIDAPI_Device *device = HIDAPI_GetDeviceByIndex(device_index); - if (!device->driver->Init(joystick, hwdata->dev, device->vendor_id, device->product_id, &hwdata->context)) { - hid_close(hwdata->dev); - SDL_free(hwdata); + if (!device->driver->OpenJoystick(&device->devdata, joystick)) { return -1; } - joystick->hwdata = hwdata; + joystick->hwdata = (struct joystick_hwdata *)device; return 0; } static int HIDAPI_JoystickRumble(SDL_Joystick * joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms) { - struct joystick_hwdata *hwdata = joystick->hwdata; - SDL_HIDAPI_DeviceDriver *driver = hwdata->driver; + SDL_HIDAPI_Device *device = (SDL_HIDAPI_Device *)joystick->hwdata; int result; - SDL_LockMutex(hwdata->mutex); - result = driver->Rumble(joystick, hwdata->dev, hwdata->context, low_frequency_rumble, high_frequency_rumble, duration_ms); - SDL_UnlockMutex(hwdata->mutex); + SDL_LockMutex(device->mutex); + result = device->driver->Rumble(&device->devdata, joystick, low_frequency_rumble, high_frequency_rumble, duration_ms); + SDL_UnlockMutex(device->mutex); return result; } static void HIDAPI_JoystickUpdate(SDL_Joystick * joystick) { - struct joystick_hwdata *hwdata = joystick->hwdata; - SDL_HIDAPI_DeviceDriver *driver = hwdata->driver; - SDL_bool succeeded; - - SDL_LockMutex(hwdata->mutex); - succeeded = driver->Update(joystick, hwdata->dev, hwdata->context); - SDL_UnlockMutex(hwdata->mutex); - - if (!succeeded) { - SDL_HIDAPI_Device *device; - for (device = SDL_HIDAPI_devices; device; device = device->next) { - if (device->instance_id == joystick->instance_id) { - HIDAPI_DelDevice(device, SDL_TRUE); - break; - } - } - } + /* No-op, all updates are done in SDL_HIDAPI_UpdateDevices */ } static void HIDAPI_JoystickClose(SDL_Joystick * joystick) { - struct joystick_hwdata *hwdata = joystick->hwdata; - SDL_HIDAPI_DeviceDriver *driver = hwdata->driver; - driver->Quit(joystick, hwdata->dev, hwdata->context); - - hid_close(hwdata->dev); - SDL_DestroyMutex(hwdata->mutex); - SDL_free(hwdata); joystick->hwdata = NULL; } @@ -1050,6 +1033,30 @@ HIDAPI_JoystickQuit(void) hid_exit(); } +void +SDL_HIDAPI_UpdateDevices(void) +{ + SDL_HIDAPI_Device *next, *device = SDL_HIDAPI_devices; + SDL_bool succeeded; + + while (device) { + if (device->driver) { + SDL_LockMutex(device->mutex); + succeeded = device->driver->UpdateDriver(&device->devdata, &SDL_HIDAPI_numjoysticks); + SDL_UnlockMutex(device->mutex); + if (!succeeded) { + next = device->next; + HIDAPI_DelDevice(device, SDL_TRUE); + device = next; + } else { + device = device->next; + } + } else { + device = device->next; + } + } +} + SDL_JoystickDriver SDL_HIDAPI_JoystickDriver = { HIDAPI_JoystickInit, diff --git a/src/joystick/hidapi/SDL_hidapijoystick_c.h b/src/joystick/hidapi/SDL_hidapijoystick_c.h index a8e707393c219..807a301cd9b72 100644 --- a/src/joystick/hidapi/SDL_hidapijoystick_c.h +++ b/src/joystick/hidapi/SDL_hidapijoystick_c.h @@ -30,6 +30,7 @@ #define SDL_JOYSTICK_HIDAPI_SWITCH #define SDL_JOYSTICK_HIDAPI_XBOX360 #define SDL_JOYSTICK_HIDAPI_XBOXONE +#define SDL_JOYSTICK_HIDAPI_GAMECUBE #ifdef __WINDOWS__ /* On Windows, Xbox One controllers are handled by the Xbox 360 driver */ @@ -43,16 +44,36 @@ #undef SDL_JOYSTICK_HIDAPI_XBOXONE #endif +typedef struct _SDL_HIDAPI_DriverData +{ + hid_device *device; + void *context; +} SDL_HIDAPI_DriverData; + typedef struct _SDL_HIDAPI_DeviceDriver { const char *hint; SDL_bool enabled; SDL_bool (*IsSupportedDevice)(Uint16 vendor_id, Uint16 product_id, Uint16 version, int interface_number); const char *(*GetDeviceName)(Uint16 vendor_id, Uint16 product_id); - SDL_bool (*Init)(SDL_Joystick *joystick, hid_device *dev, Uint16 vendor_id, Uint16 product_id, void **context); - int (*Rumble)(SDL_Joystick *joystick, hid_device *dev, void *context, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms); - SDL_bool (*Update)(SDL_Joystick *joystick, hid_device *dev, void *context); - void (*Quit)(SDL_Joystick *joystick, hid_device *dev, void *context); + + SDL_bool (*InitDriver)(SDL_HIDAPI_DriverData *context, + Uint16 vendor_id, Uint16 product_id, int *num_joysticks); + void (*QuitDriver)(SDL_HIDAPI_DriverData *context, + SDL_bool send_event, + int *num_joysticks); + SDL_bool (*UpdateDriver)(SDL_HIDAPI_DriverData *context, + int *num_joysticks); + int (*NumJoysticks)(SDL_HIDAPI_DriverData *context); + SDL_JoystickID (*InstanceIDForIndex)(SDL_HIDAPI_DriverData *context, + int index); + SDL_bool (*OpenJoystick)(SDL_HIDAPI_DriverData *context, + SDL_Joystick *joystick); + int (*Rumble)(SDL_HIDAPI_DriverData *context, + SDL_Joystick *joystick, + Uint16 low_frequency_rumble, + Uint16 high_frequency_rumble, + Uint32 duration_ms); } SDL_HIDAPI_DeviceDriver; @@ -62,6 +83,7 @@ extern SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverSteam; extern SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverSwitch; extern SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverXbox360; extern SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverXboxOne; +extern SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverGameCube; /* Return true if a HID device is present and supported as a joystick */ extern SDL_bool HIDAPI_IsDevicePresent(Uint16 vendor_id, Uint16 product_id, Uint16 version);