diff --git a/src/joystick/hidapi/SDL_hidapi_ps4.c b/src/joystick/hidapi/SDL_hidapi_ps4.c index 9d005b063..5a32ed8de 100644 --- a/src/joystick/hidapi/SDL_hidapi_ps4.c +++ b/src/joystick/hidapi/SDL_hidapi_ps4.c @@ -193,10 +193,16 @@ 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_InitDevice(SDL_HIDAPI_Device *device) +{ + return HIDAPI_JoystickConnected(device, NULL); +} + +static int HIDAPI_DriverPS4_RumbleJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, 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_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick) { SDL_DriverPS4_Context *ctx; @@ -205,14 +211,21 @@ HIDAPI_DriverPS4_Init(SDL_Joystick *joystick, hid_device *dev, Uint16 vendor_id, SDL_OutOfMemory(); return SDL_FALSE; } - *context = ctx; + + device->dev = hid_open_path(device->path, 0); + if (!device->dev) { + SDL_free(ctx); + SDL_SetError("Couldn't open %s", device->path); + return SDL_FALSE; + } + device->context = ctx; /* Check for type of connection */ - ctx->is_dongle = (vendor_id == SONY_USB_VID && product_id == SONY_DS4_DONGLE_PID); + ctx->is_dongle = (device->vendor_id == SONY_USB_VID && device->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); + } else if (device->vendor_id == SONY_USB_VID) { + ctx->is_bluetooth = !CheckUSBConnected(device->dev); } else { /* Third party controllers appear to all be wired */ ctx->is_bluetooth = SDL_FALSE; @@ -222,12 +235,12 @@ HIDAPI_DriverPS4_Init(SDL_Joystick *joystick, hid_device *dev, Uint16 vendor_id, #endif /* Check to see if audio is supported */ - if (vendor_id == SONY_USB_VID && - (product_id == SONY_DS4_SLIM_PID || product_id == SONY_DS4_DONGLE_PID )) { + if (device->vendor_id == SONY_USB_VID && + (device->product_id == SONY_DS4_SLIM_PID || device->product_id == SONY_DS4_DONGLE_PID )) { ctx->audio_supported = SDL_TRUE; } - if (HIDAPI_DriverPS4_CanRumble(vendor_id, product_id)) { + if (HIDAPI_DriverPS4_CanRumble(device->vendor_id, device->product_id)) { if (ctx->is_bluetooth) { ctx->rumble_supported = SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI_PS4_RUMBLE, SDL_FALSE); } else { @@ -236,7 +249,7 @@ HIDAPI_DriverPS4_Init(SDL_Joystick *joystick, hid_device *dev, Uint16 vendor_id, } /* Initialize LED and effect state */ - HIDAPI_DriverPS4_Rumble(joystick, dev, ctx, 0, 0, 0); + HIDAPI_DriverPS4_RumbleJoystick(device, joystick, 0, 0, 0); /* Initialize the joystick capabilities */ joystick->nbuttons = 16; @@ -247,9 +260,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_RumbleJoystick(SDL_HIDAPI_Device *device, 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 *)device->context; DS4EffectsState_t *effects; Uint8 data[78]; int report_size, offset; @@ -293,7 +306,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(device->dev, data, report_size) != report_size) { return SDL_SetError("Couldn't send rumble packet"); } @@ -416,20 +429,28 @@ 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_UpdateDevice(SDL_HIDAPI_Device *device) { - SDL_DriverPS4_Context *ctx = (SDL_DriverPS4_Context *)context; + SDL_DriverPS4_Context *ctx = (SDL_DriverPS4_Context *)device->context; + SDL_Joystick *joystick = NULL; Uint8 data[USB_PACKET_LENGTH]; int size; - while ((size = hid_read_timeout(dev, data, sizeof(data), 0)) > 0) { + if (device->num_joysticks > 0) { + joystick = SDL_JoystickFromInstanceID(device->joysticks[0]); + } + if (!joystick) { + return SDL_FALSE; + } + + while ((size = hid_read_timeout(device->dev, 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, device->dev, 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, device->dev, ctx, (PS4StatePacket_t *)&data[3]); break; default: #ifdef DEBUG_JOYSTICK @@ -442,17 +463,30 @@ 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_RumbleJoystick(device, joystick, 0, 0, 0); } } + if (size < 0) { + /* Read error, device is disconnected */ + HIDAPI_JoystickDisconnected(device, joystick->instance_id); + } return (size >= 0); } static void -HIDAPI_DriverPS4_Quit(SDL_Joystick *joystick, hid_device *dev, void *context) +HIDAPI_DriverPS4_CloseJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick) +{ + hid_close(device->dev); + device->dev = NULL; + + SDL_free(device->context); + device->context = NULL; +} + +static void +HIDAPI_DriverPS4_FreeDevice(SDL_HIDAPI_Device *device) { - SDL_free(context); } SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverPS4 = @@ -461,10 +495,12 @@ SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverPS4 = SDL_TRUE, HIDAPI_DriverPS4_IsSupportedDevice, HIDAPI_DriverPS4_GetDeviceName, - HIDAPI_DriverPS4_Init, - HIDAPI_DriverPS4_Rumble, - HIDAPI_DriverPS4_Update, - HIDAPI_DriverPS4_Quit + HIDAPI_DriverPS4_InitDevice, + HIDAPI_DriverPS4_UpdateDevice, + HIDAPI_DriverPS4_OpenJoystick, + HIDAPI_DriverPS4_RumbleJoystick, + HIDAPI_DriverPS4_CloseJoystick, + HIDAPI_DriverPS4_FreeDevice }; #endif /* SDL_JOYSTICK_HIDAPI_PS4 */ diff --git a/src/joystick/hidapi/SDL_hidapi_switch.c b/src/joystick/hidapi/SDL_hidapi_switch.c index 09c7fd0fd..0cc01574e 100644 --- a/src/joystick/hidapi/SDL_hidapi_switch.c +++ b/src/joystick/hidapi/SDL_hidapi_switch.c @@ -223,6 +223,23 @@ typedef struct { } SDL_DriverSwitch_Context; +static SDL_bool IsGameCubeFormFactor(int vendor_id, int product_id) +{ + static Uint32 gamecube_formfactor[] = { + MAKE_VIDPID(0x0e6f, 0x0185), /* PDP Wired Fight Pad Pro for Nintendo Switch */ + MAKE_VIDPID(0x20d6, 0xa711), /* Core (Plus) Wired Controller */ + }; + Uint32 id = MAKE_VIDPID(vendor_id, product_id); + int i; + + for (i = 0; i < SDL_arraysize(gamecube_formfactor); ++i) { + if (id == gamecube_formfactor[i]) { + return SDL_TRUE; + } + } + return SDL_FALSE; +} + static SDL_bool HIDAPI_DriverSwitch_IsSupportedDevice(Uint16 vendor_id, Uint16 product_id, Uint16 version, int interface_number, const char *name) { @@ -604,9 +621,15 @@ static Uint8 RemapButton(SDL_DriverSwitch_Context *ctx, Uint8 button) } return button; } - + static SDL_bool -HIDAPI_DriverSwitch_Init(SDL_Joystick *joystick, hid_device *dev, Uint16 vendor_id, Uint16 product_id, void **context) +HIDAPI_DriverSwitch_InitDevice(SDL_HIDAPI_Device *device) +{ + return HIDAPI_JoystickConnected(device, NULL); +} + +static SDL_bool +HIDAPI_DriverSwitch_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick) { SDL_DriverSwitch_Context *ctx; Uint8 input_mode; @@ -616,15 +639,19 @@ HIDAPI_DriverSwitch_Init(SDL_Joystick *joystick, hid_device *dev, Uint16 vendor_ SDL_OutOfMemory(); return SDL_FALSE; } - ctx->dev = dev; - *context = ctx; + device->dev = ctx->dev = hid_open_path(device->path, 0); + if (!device->dev) { + SDL_SetError("Couldn't open %s", device->path); + goto error; + } + device->context = ctx; /* Find out whether or not we can send output reports */ - ctx->m_bInputOnly = SDL_IsJoystickNintendoSwitchProInputOnly(vendor_id, product_id); + ctx->m_bInputOnly = SDL_IsJoystickNintendoSwitchProInputOnly(device->vendor_id, device->product_id); if (!ctx->m_bInputOnly) { /* The Power A Nintendo Switch Pro controllers don't have a Home LED */ - ctx->m_bHasHomeLED = (vendor_id != 0 && product_id != 0) ? SDL_TRUE : SDL_FALSE; + ctx->m_bHasHomeLED = (device->vendor_id != 0 && device->product_id != 0) ? SDL_TRUE : SDL_FALSE; /* Initialize rumble data */ SetNeutralRumble(&ctx->m_RumblePacket.rumbleData[0]); @@ -637,14 +664,12 @@ HIDAPI_DriverSwitch_Init(SDL_Joystick *joystick, hid_device *dev, Uint16 vendor_ if (!LoadStickCalibration(ctx)) { SDL_SetError("Couldn't load stick calibration"); - SDL_free(ctx); - return SDL_FALSE; + goto error; } if (!SetVibrationEnabled(ctx, 1)) { SDL_SetError("Couldn't enable vibration"); - SDL_free(ctx); - return SDL_FALSE; + goto error; } /* Set the desired input mode */ @@ -655,8 +680,7 @@ HIDAPI_DriverSwitch_Init(SDL_Joystick *joystick, hid_device *dev, Uint16 vendor_ } if (!SetInputMode(ctx, input_mode)) { SDL_SetError("Couldn't set input mode"); - SDL_free(ctx); - return SDL_FALSE; + goto error; } /* Start sending USB reports */ @@ -664,8 +688,7 @@ HIDAPI_DriverSwitch_Init(SDL_Joystick *joystick, hid_device *dev, Uint16 vendor_ /* ForceUSB doesn't generate an ACK, so don't wait for a reply */ if (!WriteProprietary(ctx, k_eSwitchProprietaryCommandIDs_ForceUSB, NULL, 0, SDL_FALSE)) { SDL_SetError("Couldn't start USB reports"); - SDL_free(ctx); - return SDL_FALSE; + goto error; } } @@ -676,7 +699,7 @@ HIDAPI_DriverSwitch_Init(SDL_Joystick *joystick, hid_device *dev, Uint16 vendor_ SetSlotLED(ctx, (joystick->instance_id % 4)); } - if (vendor_id == 0x0e6f && product_id == 0x0185) { + if (IsGameCubeFormFactor(device->vendor_id, device->product_id)) { /* This is a controller shaped like a GameCube controller, with a large central A button */ ctx->m_bUseButtonLabels = SDL_TRUE; } else { @@ -690,12 +713,23 @@ HIDAPI_DriverSwitch_Init(SDL_Joystick *joystick, hid_device *dev, Uint16 vendor_ joystick->epowerlevel = SDL_JOYSTICK_POWER_WIRED; return SDL_TRUE; + +error: + if (device->dev) { + hid_close(device->dev); + device->dev = NULL; + } + if (device->context) { + SDL_free(device->context); + device->context = NULL; + } + return SDL_FALSE; } 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_RumbleJoystick(SDL_HIDAPI_Device *device, 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 *)device->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 @@ -996,11 +1030,19 @@ 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_UpdateDevice(SDL_HIDAPI_Device *device) { - SDL_DriverSwitch_Context *ctx = (SDL_DriverSwitch_Context *)context; + SDL_DriverSwitch_Context *ctx = (SDL_DriverSwitch_Context *)device->context; + SDL_Joystick *joystick = NULL; int size; + if (device->num_joysticks > 0) { + joystick = SDL_JoystickFromInstanceID(device->joysticks[0]); + } + if (!joystick) { + return SDL_FALSE; + } + while ((size = ReadInput(ctx)) > 0) { if (ctx->m_bInputOnly) { HandleInputOnlyControllerState(joystick, ctx, (SwitchInputOnlyControllerStatePacket_t *)&ctx->m_rgucReadBuffer[0]); @@ -1021,17 +1063,21 @@ 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_RumbleJoystick(device, joystick, 0, 0, 0); } } + if (size < 0) { + /* Read error, device is disconnected */ + HIDAPI_JoystickDisconnected(device, joystick->instance_id); + } return (size >= 0); } static void -HIDAPI_DriverSwitch_Quit(SDL_Joystick *joystick, hid_device *dev, void *context) +HIDAPI_DriverSwitch_CloseJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick) { - SDL_DriverSwitch_Context *ctx = (SDL_DriverSwitch_Context *)context; + SDL_DriverSwitch_Context *ctx = (SDL_DriverSwitch_Context *)device->context; if (!ctx->m_bInputOnly) { /* Restore simple input mode for other applications */ @@ -1041,7 +1087,16 @@ HIDAPI_DriverSwitch_Quit(SDL_Joystick *joystick, hid_device *dev, void *context) SDL_DelHintCallback(SDL_HINT_GAMECONTROLLER_USE_BUTTON_LABELS, SDL_GameControllerButtonReportingHintChanged, ctx); - SDL_free(context); + hid_close(device->dev); + device->dev = NULL; + + SDL_free(device->context); + device->context = NULL; +} + +static void +HIDAPI_DriverSwitch_FreeDevice(SDL_HIDAPI_Device *device) +{ } SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverSwitch = @@ -1050,10 +1105,12 @@ 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_InitDevice, + HIDAPI_DriverSwitch_UpdateDevice, + HIDAPI_DriverSwitch_OpenJoystick, + HIDAPI_DriverSwitch_RumbleJoystick, + HIDAPI_DriverSwitch_CloseJoystick, + HIDAPI_DriverSwitch_FreeDevice }; #endif /* SDL_JOYSTICK_HIDAPI_SWITCH */ diff --git a/src/joystick/hidapi/SDL_hidapi_xbox360.c b/src/joystick/hidapi/SDL_hidapi_xbox360.c index 453c4c18b..0490392b6 100644 --- a/src/joystick/hidapi/SDL_hidapi_xbox360.c +++ b/src/joystick/hidapi/SDL_hidapi_xbox360.c @@ -291,7 +291,13 @@ 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_InitDevice(SDL_HIDAPI_Device *device) +{ + return HIDAPI_JoystickConnected(device, NULL); +} + +static SDL_bool +HIDAPI_DriverXbox360_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick) { SDL_DriverXbox360_Context *ctx; @@ -300,6 +306,15 @@ HIDAPI_DriverXbox360_Init(SDL_Joystick *joystick, hid_device *dev, Uint16 vendor SDL_OutOfMemory(); return SDL_FALSE; } + + device->dev = hid_open_path(device->path, 0); + if (!device->dev) { + SDL_free(ctx); + SDL_SetError("Couldn't open %s", device->path); + return SDL_FALSE; + } + device->context = ctx; + #ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT ctx->xinput_enabled = SDL_GetHintBoolean(SDL_HINT_XINPUT_ENABLED, SDL_TRUE); if (ctx->xinput_enabled && WIN_LoadXInputDLL() < 0) { @@ -310,10 +325,9 @@ 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; /* Set the controller LED */ - SetSlotLED(dev, (joystick->instance_id % 4)); + SetSlotLED(device->dev, (joystick->instance_id % 4)); /* Initialize the joystick capabilities */ joystick->nbuttons = SDL_CONTROLLER_BUTTON_MAX; @@ -324,9 +338,9 @@ 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_RumbleJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms) { - SDL_DriverXbox360_Context *ctx = (SDL_DriverXbox360_Context *)context; + SDL_DriverXbox360_Context *ctx = (SDL_DriverXbox360_Context *)device->context; #ifdef __WIN32__ SDL_bool rumbled = SDL_FALSE; @@ -379,7 +393,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(device->dev, rumble_packet, sizeof(rumble_packet)) != sizeof(rumble_packet)) { return SDL_SetError("Couldn't send rumble packet"); } #endif /* __WIN32__ */ @@ -719,26 +733,34 @@ 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_UpdateDevice(SDL_HIDAPI_Device *device) { - SDL_DriverXbox360_Context *ctx = (SDL_DriverXbox360_Context *)context; + SDL_DriverXbox360_Context *ctx = (SDL_DriverXbox360_Context *)device->context; + SDL_Joystick *joystick = NULL; Uint8 data[USB_PACKET_LENGTH]; int size; - while ((size = hid_read_timeout(dev, data, sizeof(data), 0)) > 0) { + if (device->num_joysticks > 0) { + joystick = SDL_JoystickFromInstanceID(device->joysticks[0]); + } + if (!joystick) { + return SDL_FALSE; + } + + while ((size = hid_read_timeout(device->dev, data, sizeof(data), 0)) > 0) { #ifdef __WIN32__ - HIDAPI_DriverXbox360_HandleStatePacket(joystick, dev, ctx, data, size); + HIDAPI_DriverXbox360_HandleStatePacket(joystick, device->dev, ctx, data, size); #else switch (data[0]) { case 0x00: - HIDAPI_DriverXbox360_HandleStatePacket(joystick, dev, ctx, data, size); + HIDAPI_DriverXbox360_HandleStatePacket(joystick, device->dev, ctx, data, size); break; #ifdef __MACOSX__ case 0x01: - HIDAPI_DriverXboxOneS_HandleStatePacket(joystick, dev, ctx, data, size); + HIDAPI_DriverXboxOneS_HandleStatePacket(joystick, device->dev, ctx, data, size); break; case 0x02: - HIDAPI_DriverXboxOneS_HandleGuidePacket(joystick, dev, ctx, data, size); + HIDAPI_DriverXboxOneS_HandleGuidePacket(joystick, device->dev, ctx, data, size); break; #endif default: @@ -756,18 +778,22 @@ 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_RumbleJoystick(device, joystick, 0, 0, 0); } } + if (size < 0) { + /* Read error, device is disconnected */ + HIDAPI_JoystickDisconnected(device, joystick->instance_id); + } return (size >= 0); } static void -HIDAPI_DriverXbox360_Quit(SDL_Joystick *joystick, hid_device *dev, void *context) +HIDAPI_DriverXbox360_CloseJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick) { #if defined(SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT) || defined(SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT) - SDL_DriverXbox360_Context *ctx = (SDL_DriverXbox360_Context *)context; + SDL_DriverXbox360_Context *ctx = (SDL_DriverXbox360_Context *)device->context; #endif #ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT @@ -777,9 +803,19 @@ HIDAPI_DriverXbox360_Quit(SDL_Joystick *joystick, hid_device *dev, void *context } #endif #ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT - HIDAPI_DriverXbox360_InitWindowsGamingInput(ctx); + HIDAPI_DriverXbox360_QuitWindowsGamingInput(ctx); #endif - SDL_free(context); + + hid_close(device->dev); + device->dev = NULL; + + SDL_free(device->context); + device->context = NULL; +} + +static void +HIDAPI_DriverXbox360_FreeDevice(SDL_HIDAPI_Device *device) +{ } SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverXbox360 = @@ -788,10 +824,12 @@ 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_InitDevice, + HIDAPI_DriverXbox360_UpdateDevice, + HIDAPI_DriverXbox360_OpenJoystick, + HIDAPI_DriverXbox360_RumbleJoystick, + HIDAPI_DriverXbox360_CloseJoystick, + HIDAPI_DriverXbox360_FreeDevice }; #endif /* SDL_JOYSTICK_HIDAPI_XBOX360 */ diff --git a/src/joystick/hidapi/SDL_hidapi_xboxone.c b/src/joystick/hidapi/SDL_hidapi_xboxone.c index 54e07e810..1ddd1a8f5 100644 --- a/src/joystick/hidapi/SDL_hidapi_xboxone.c +++ b/src/joystick/hidapi/SDL_hidapi_xboxone.c @@ -255,7 +255,13 @@ 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_InitDevice(SDL_HIDAPI_Device *device) +{ + return HIDAPI_JoystickConnected(device, NULL); +} + +static SDL_bool +HIDAPI_DriverXboxOne_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick) { SDL_DriverXboxOne_Context *ctx; @@ -264,10 +270,17 @@ HIDAPI_DriverXboxOne_Init(SDL_Joystick *joystick, hid_device *dev, Uint16 vendor SDL_OutOfMemory(); return SDL_FALSE; } - *context = ctx; - ctx->vendor_id = vendor_id; - ctx->product_id = product_id; + device->dev = hid_open_path(device->path, 0); + if (!device->dev) { + SDL_free(ctx); + SDL_SetError("Couldn't open %s", device->path); + return SDL_FALSE; + } + device->context = ctx; + + ctx->vendor_id = device->vendor_id; + ctx->product_id = device->product_id; ctx->start_time = SDL_GetTicks(); /* Initialize the joystick capabilities */ @@ -279,9 +292,9 @@ HIDAPI_DriverXboxOne_Init(SDL_Joystick *joystick, hid_device *dev, Uint16 vendor } 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_RumbleJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms) { - SDL_DriverXboxOne_Context *ctx = (SDL_DriverXboxOne_Context *)context; + SDL_DriverXboxOne_Context *ctx = (SDL_DriverXboxOne_Context *)device->context; Uint8 rumble_packet[] = { 0x09, 0x00, 0x00, 0x09, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF }; if (!ctx->initialized) { @@ -293,7 +306,7 @@ HIDAPI_DriverXboxOne_Rumble(SDL_Joystick *joystick, hid_device *dev, void *conte rumble_packet[8] = low_frequency_rumble / 655; rumble_packet[9] = high_frequency_rumble / 655; - if (hid_write(dev, rumble_packet, sizeof(rumble_packet)) != sizeof(rumble_packet)) { + if (hid_write(device->dev, rumble_packet, sizeof(rumble_packet)) != sizeof(rumble_packet)) { return SDL_SetError("Couldn't send rumble packet"); } @@ -366,22 +379,31 @@ 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_UpdateDevice(SDL_HIDAPI_Device *device) { - SDL_DriverXboxOne_Context *ctx = (SDL_DriverXboxOne_Context *)context; + SDL_DriverXboxOne_Context *ctx = (SDL_DriverXboxOne_Context *)device->context; + SDL_Joystick *joystick = NULL; Uint8 data[USB_PACKET_LENGTH]; int size; + if (device->num_joysticks > 0) { + joystick = SDL_JoystickFromInstanceID(device->joysticks[0]); + } + if (!joystick) { + return SDL_FALSE; + } + if (!ctx->initialized) { if (SDL_TICKS_PASSED(SDL_GetTicks(), ctx->start_time + CONTROLLER_INIT_DELAY_MS)) { - if (!SendControllerInit(dev, ctx)) { + if (!SendControllerInit(device->dev, ctx)) { + HIDAPI_JoystickDisconnected(device, joystick->instance_id); return SDL_FALSE; } ctx->initialized = SDL_TRUE; } } - while ((size = hid_read_timeout(dev, data, sizeof(data), 0)) > 0) { + while ((size = hid_read_timeout(device->dev, data, sizeof(data), 0)) > 0) { #ifdef DEBUG_XBOX_PROTOCOL SDL_Log("Xbox One packet: size = %d\n" " 0x%.2x 0x%.2x 0x%.2x 0x%.2x 0x%.2x 0x%.2x 0x%.2x 0x%.2x\n" @@ -394,10 +416,10 @@ HIDAPI_DriverXboxOne_Update(SDL_Joystick *joystick, hid_device *dev, void *conte #endif switch (data[0]) { case 0x20: - HIDAPI_DriverXboxOne_HandleStatePacket(joystick, dev, ctx, data, size); + HIDAPI_DriverXboxOne_HandleStatePacket(joystick, device->dev, ctx, data, size); break; case 0x07: - HIDAPI_DriverXboxOne_HandleModePacket(joystick, dev, ctx, data, size); + HIDAPI_DriverXboxOne_HandleModePacket(joystick, device->dev, ctx, data, size); break; default: #ifdef DEBUG_JOYSTICK @@ -410,17 +432,30 @@ 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_RumbleJoystick(device, joystick, 0, 0, 0); } } + if (size < 0) { + /* Read error, device is disconnected */ + HIDAPI_JoystickDisconnected(device, joystick->instance_id); + } return (size >= 0); } static void -HIDAPI_DriverXboxOne_Quit(SDL_Joystick *joystick, hid_device *dev, void *context) +HIDAPI_DriverXboxOne_CloseJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick) +{ + hid_close(device->dev); + device->dev = NULL; + + SDL_free(device->context); + device->context = NULL; +} + +static void +HIDAPI_DriverXboxOne_FreeDevice(SDL_HIDAPI_Device *device) { - SDL_free(context); } SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverXboxOne = @@ -429,10 +464,12 @@ SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverXboxOne = SDL_TRUE, HIDAPI_DriverXboxOne_IsSupportedDevice, HIDAPI_DriverXboxOne_GetDeviceName, - HIDAPI_DriverXboxOne_Init, - HIDAPI_DriverXboxOne_Rumble, - HIDAPI_DriverXboxOne_Update, - HIDAPI_DriverXboxOne_Quit + HIDAPI_DriverXboxOne_InitDevice, + HIDAPI_DriverXboxOne_UpdateDevice, + HIDAPI_DriverXboxOne_OpenJoystick, + HIDAPI_DriverXboxOne_RumbleJoystick, + HIDAPI_DriverXboxOne_CloseJoystick, + HIDAPI_DriverXboxOne_FreeDevice }; #endif /* SDL_JOYSTICK_HIDAPI_XBOXONE */ diff --git a/src/joystick/hidapi/SDL_hidapijoystick.c b/src/joystick/hidapi/SDL_hidapijoystick.c index 328a84c3b..929acbd5f 100644 --- a/src/joystick/hidapi/SDL_hidapijoystick.c +++ b/src/joystick/hidapi/SDL_hidapijoystick.c @@ -22,6 +22,7 @@ #ifdef SDL_JOYSTICK_HIDAPI +#include "SDL_assert.h" #include "SDL_endian.h" #include "SDL_hints.h" #include "SDL_log.h" @@ -53,33 +54,9 @@ struct joystick_hwdata { - SDL_HIDAPI_DeviceDriver *driver; - void *context; - - SDL_mutex *mutex; - hid_device *dev; + SDL_HIDAPI_Device *device; }; -typedef struct _SDL_HIDAPI_Device -{ - SDL_JoystickID instance_id; - char *name; - char *path; - Uint16 vendor_id; - Uint16 product_id; - Uint16 version; - SDL_JoystickGUID guid; - int interface_number; /* Available on Windows and Linux */ - Uint16 usage_page; /* Available on Windows and Mac OS X */ - Uint16 usage; /* Available on Windows and Mac OS X */ - SDL_HIDAPI_DeviceDriver *driver; - - /* Used during scanning for device changes */ - SDL_bool seen; - - struct _SDL_HIDAPI_Device *next; -} SDL_HIDAPI_Device; - static SDL_HIDAPI_DeviceDriver *SDL_HIDAPI_drivers[] = { #ifdef SDL_JOYSTICK_HIDAPI_PS4 &SDL_HIDAPI_DriverPS4, @@ -98,9 +75,11 @@ static SDL_HIDAPI_DeviceDriver *SDL_HIDAPI_drivers[] = { #endif }; static int SDL_HIDAPI_numdrivers = 0; +static SDL_mutex *SDL_HIDAPI_mutex; static SDL_HIDAPI_Device *SDL_HIDAPI_devices; static int SDL_HIDAPI_numjoysticks = 0; static SDL_bool initialized = SDL_FALSE; +static SDL_bool shutting_down = SDL_FALSE; #if defined(SDL_USE_LIBUDEV) static const SDL_UDEV_Symbols * usyms = NULL; @@ -396,6 +375,9 @@ HIDAPI_ShutdownDiscovery() #endif } +static void HIDAPI_JoystickDetect(void); +static void HIDAPI_JoystickClose(SDL_Joystick * joystick); + static SDL_bool HIDAPI_IsDeviceSupported(Uint16 vendor_id, Uint16 product_id, Uint16 version, const char *name) { @@ -440,19 +422,22 @@ HIDAPI_GetDeviceDriver(SDL_HIDAPI_Device *device) } static SDL_HIDAPI_Device * -HIDAPI_GetJoystickByIndex(int device_index) +HIDAPI_GetDeviceByIndex(int device_index, SDL_JoystickID *pJoystickID) { SDL_HIDAPI_Device *device = SDL_HIDAPI_devices; while (device) { if (device->driver) { - if (device_index == 0) { - break; + if (device_index < device->num_joysticks) { + if (pJoystickID) { + *pJoystickID = device->joysticks[device_index]; + } + return device; } - --device_index; + device_index -= device->num_joysticks; } device = device->next; } - return device; + return NULL; } static SDL_HIDAPI_Device * @@ -469,6 +454,52 @@ HIDAPI_GetJoystickByInfo(const char *path, Uint16 vendor_id, Uint16 product_id) return device; } +static void +HIDAPI_SetupDeviceDriver(SDL_HIDAPI_Device *device) +{ + if (device->driver) { + /* Already setup */ + return; + } + + device->driver = HIDAPI_GetDeviceDriver(device); + if (device->driver) { + const char *name = device->driver->GetDeviceName(device->vendor_id, device->product_id); + if (name) { + SDL_free(device->name); + device->name = SDL_strdup(name); + } + } + + /* Initialize the device, which may cause a connected event */ + if (device->driver && !device->driver->InitDevice(device)) { + device->driver = NULL; + } +} + +static void +HIDAPI_CleanupDeviceDriver(SDL_HIDAPI_Device *device) +{ + int i; + + if (!device->driver) { + /* Already cleaned up */ + return; + } + + /* Disconnect any joysticks */ + for (i = 0; i < device->num_joysticks; ++i) { + SDL_Joystick *joystick = SDL_JoystickFromInstanceID(device->joysticks[i]); + if (joystick) { + HIDAPI_JoystickClose(joystick); + } + HIDAPI_JoystickDisconnected(device, device->joysticks[i]); + } + + device->driver->FreeDevice(device); + device->driver = NULL; +} + static void SDLCALL SDL_HIDAPIDriverHintChanged(void *userdata, const char *name, const char *oldValue, const char *hint) { @@ -500,30 +531,18 @@ SDL_HIDAPIDriverHintChanged(void *userdata, const char *name, const char *oldVal } /* Update device list if driver availability changes */ + SDL_LockMutex(SDL_HIDAPI_mutex); + while (device) { - if (device->driver) { - if (!device->driver->enabled) { - device->driver = NULL; - - --SDL_HIDAPI_numjoysticks; - - SDL_PrivateJoystickRemoved(device->instance_id); - } - } else { - device->driver = HIDAPI_GetDeviceDriver(device); - if (device->driver) { - device->instance_id = SDL_GetNextJoystickInstanceID(); - - ++SDL_HIDAPI_numjoysticks; - - SDL_PrivateJoystickAdded(device->instance_id); - } + if (device->driver && !device->driver->enabled) { + HIDAPI_CleanupDeviceDriver(device); } + HIDAPI_SetupDeviceDriver(device); device = device->next; } -} -static void HIDAPI_JoystickDetect(void); + SDL_UnlockMutex(SDL_HIDAPI_mutex); +} static int HIDAPI_JoystickInit(void) @@ -539,6 +558,8 @@ HIDAPI_JoystickInit(void) return -1; } + SDL_HIDAPI_mutex = SDL_CreateMutex(); + for (i = 0; i < SDL_arraysize(SDL_HIDAPI_drivers); ++i) { SDL_HIDAPI_DeviceDriver *driver = SDL_HIDAPI_drivers[i]; SDL_AddHintCallback(driver->hint, SDL_HIDAPIDriverHintChanged, NULL); @@ -553,6 +574,47 @@ HIDAPI_JoystickInit(void) return 0; } +SDL_bool +HIDAPI_JoystickConnected(SDL_HIDAPI_Device *device, SDL_JoystickID *pJoystickID) +{ + SDL_JoystickID joystickID; + SDL_JoystickID *joysticks = (SDL_JoystickID *)SDL_realloc(device->joysticks, (device->num_joysticks + 1)*sizeof(*device->joysticks)); + if (!joysticks) { + return SDL_FALSE; + } + + joystickID = SDL_GetNextJoystickInstanceID(); + device->joysticks = joysticks; + device->joysticks[device->num_joysticks++] = joystickID; + ++SDL_HIDAPI_numjoysticks; + + SDL_PrivateJoystickAdded(joystickID); + + if (pJoystickID) { + *pJoystickID = joystickID; + } + return SDL_TRUE; +} + +void +HIDAPI_JoystickDisconnected(SDL_HIDAPI_Device *device, SDL_JoystickID joystickID) +{ + int i; + + for (i = 0; i < device->num_joysticks; ++i) { + if (device->joysticks[i] == joystickID) { + SDL_memcpy(&device->joysticks[i], &device->joysticks[i+1], device->num_joysticks - i - 1); + --device->num_joysticks; + --SDL_HIDAPI_numjoysticks; + + if (!shutting_down) { + SDL_PrivateJoystickRemoved(joystickID); + } + return; + } + } +} + static int HIDAPI_JoystickGetCount(void) { @@ -573,7 +635,11 @@ HIDAPI_AddDevice(struct hid_device_info *info) if (!device) { return; } - device->instance_id = -1; + device->path = SDL_strdup(info->path); + if (!device->path) { + SDL_free(device); + return; + } device->seen = SDL_TRUE; device->vendor_id = info->vendor_id; device->product_id = info->product_id; @@ -651,33 +717,13 @@ HIDAPI_AddDevice(struct hid_device_info *info) size_t name_size = (6 + 1 + 6 + 1); device->name = (char *)SDL_malloc(name_size); if (!device->name) { + SDL_free(device->path); SDL_free(device); return; } SDL_snprintf(device->name, name_size, "0x%.4x/0x%.4x", info->vendor_id, info->product_id); } - device->driver = HIDAPI_GetDeviceDriver(device); - - if (device->driver) { - const char *name = device->driver->GetDeviceName(device->vendor_id, device->product_id); - if (name) { - SDL_free(device->name); - device->name = SDL_strdup(name); - } - } - - device->path = SDL_strdup(info->path); - if (!device->path) { - SDL_free(device->name); - SDL_free(device); - return; - } - -#ifdef DEBUG_HIDAPI - SDL_Log("Adding HIDAPI device '%s' VID 0x%.4x, PID 0x%.4x, version %d, interface %d, usage page 0x%.4x, usage 0x%.4x, driver = %s\n", device->name, device->vendor_id, device->product_id, device->version, device->interface_number, device->usage_page, device->usage, device->driver ? device->driver->hint : "NONE"); -#endif - /* Add it to the list */ if (last) { last->next = device; @@ -685,19 +731,16 @@ HIDAPI_AddDevice(struct hid_device_info *info) SDL_HIDAPI_devices = device; } - if (device->driver) { - /* It's a joystick! */ - device->instance_id = SDL_GetNextJoystickInstanceID(); +#ifdef DEBUG_HIDAPI + SDL_Log("Added HIDAPI device '%s' VID 0x%.4x, PID 0x%.4x, version %d, interface %d, usage page 0x%.4x, usage 0x%.4x, driver = %s\n", device->name, device->vendor_id, device->product_id, device->version, device->interface_number, device->usage_page, device->usage, device->driver ? device->driver->hint : "NONE"); +#endif - ++SDL_HIDAPI_numjoysticks; - - SDL_PrivateJoystickAdded(device->instance_id); - } + HIDAPI_SetupDeviceDriver(device); } static void -HIDAPI_DelDevice(SDL_HIDAPI_Device *device, SDL_bool send_event) +HIDAPI_DelDevice(SDL_HIDAPI_Device *device) { SDL_HIDAPI_Device *curr, *last; for (curr = SDL_HIDAPI_devices, last = NULL; curr; last = curr, curr = curr->next) { @@ -708,12 +751,7 @@ 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); - } + HIDAPI_CleanupDeviceDriver(device); SDL_free(device->name); SDL_free(device->path); @@ -729,6 +767,8 @@ HIDAPI_UpdateDeviceList(void) SDL_HIDAPI_Device *device; struct hid_device_info *devs, *info; + SDL_LockMutex(SDL_HIDAPI_mutex); + /* Prepare the existing device list */ device = SDL_HIDAPI_devices; while (device) { @@ -758,10 +798,12 @@ HIDAPI_UpdateDeviceList(void) SDL_HIDAPI_Device *next = device->next; if (!device->seen) { - HIDAPI_DelDevice(device, SDL_TRUE); + HIDAPI_DelDevice(device); } device = next; } + + SDL_UnlockMutex(SDL_HIDAPI_mutex); } SDL_bool @@ -799,18 +841,45 @@ HIDAPI_IsDevicePresent(Uint16 vendor_id, Uint16 product_id, Uint16 version, cons static void HIDAPI_JoystickDetect(void) { + SDL_HIDAPI_Device *device; + HIDAPI_UpdateDiscovery(); if (SDL_HIDAPI_discovery.m_bHaveDevicesChanged) { /* FIXME: We probably need to schedule an update in a few seconds as well */ HIDAPI_UpdateDeviceList(); SDL_HIDAPI_discovery.m_bHaveDevicesChanged = SDL_FALSE; } + + /* Update the devices, which may change connected joysticks and send events */ + SDL_LockMutex(SDL_HIDAPI_mutex); + + /* Prepare the existing device list */ + device = SDL_HIDAPI_devices; + while (device) { + if (device->driver) { + device->driver->UpdateDevice(device); + } + device = device->next; + } + + SDL_UnlockMutex(SDL_HIDAPI_mutex); } static const char * HIDAPI_JoystickGetDeviceName(int device_index) { - return HIDAPI_GetJoystickByIndex(device_index)->name; + SDL_HIDAPI_Device *device; + const char *name = NULL; + + SDL_LockMutex(SDL_HIDAPI_mutex); + device = HIDAPI_GetDeviceByIndex(device_index, NULL); + if (device) { + /* FIXME: The device could be freed after this name is returned... */ + name = device->name; + } + SDL_UnlockMutex(SDL_HIDAPI_mutex); + + return name; } static int @@ -822,36 +891,45 @@ HIDAPI_JoystickGetDevicePlayerIndex(int device_index) static SDL_JoystickGUID HIDAPI_JoystickGetDeviceGUID(int device_index) { - return HIDAPI_GetJoystickByIndex(device_index)->guid; + SDL_HIDAPI_Device *device; + SDL_JoystickGUID guid; + + SDL_LockMutex(SDL_HIDAPI_mutex); + device = HIDAPI_GetDeviceByIndex(device_index, NULL); + if (device) { + SDL_memcpy(&guid, &device->guid, sizeof(guid)); + } else { + SDL_zero(guid); + } + SDL_UnlockMutex(SDL_HIDAPI_mutex); + + return guid; } static SDL_JoystickID HIDAPI_JoystickGetDeviceInstanceID(int device_index) { - return HIDAPI_GetJoystickByIndex(device_index)->instance_id; + SDL_JoystickID joystickID; + SDL_LockMutex(SDL_HIDAPI_mutex); + HIDAPI_GetDeviceByIndex(device_index, &joystickID); + SDL_UnlockMutex(SDL_HIDAPI_mutex); + return joystickID; } static int HIDAPI_JoystickOpen(SDL_Joystick * joystick, int device_index) { - SDL_HIDAPI_Device *device = HIDAPI_GetJoystickByIndex(device_index); + SDL_JoystickID joystickID; + SDL_HIDAPI_Device *device = HIDAPI_GetDeviceByIndex(device_index, &joystickID); struct joystick_hwdata *hwdata; hwdata = (struct joystick_hwdata *)SDL_calloc(1, sizeof(*hwdata)); if (!hwdata) { return SDL_OutOfMemory(); } + hwdata->device = device; - 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(); - - if (!device->driver->Init(joystick, hwdata->dev, device->vendor_id, device->product_id, &hwdata->context)) { - hid_close(hwdata->dev); + if (!device->driver->OpenJoystick(device, joystick)) { SDL_free(hwdata); return -1; } @@ -863,49 +941,41 @@ HIDAPI_JoystickOpen(SDL_Joystick * joystick, int device_index) 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; 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(SDL_HIDAPI_mutex); + if (joystick->hwdata) { + SDL_HIDAPI_Device *device = joystick->hwdata->device; + + result = device->driver->RumbleJoystick(device, joystick, low_frequency_rumble, high_frequency_rumble, duration_ms); + } else { + SDL_SetError("Rumble failed, device disconnected"); + result = -1; + } + SDL_UnlockMutex(SDL_HIDAPI_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; - } - } - } + /* This is handled in HIDAPI_JoystickDetect() */ } 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); + SDL_LockMutex(SDL_HIDAPI_mutex); + if (joystick->hwdata) { + SDL_HIDAPI_Device *device = joystick->hwdata->device; - hid_close(hwdata->dev); - SDL_DestroyMutex(hwdata->mutex); - SDL_free(hwdata); - joystick->hwdata = NULL; + device->driver->CloseJoystick(device, joystick); + + SDL_free(joystick->hwdata); + joystick->hwdata = NULL; + } + SDL_UnlockMutex(SDL_HIDAPI_mutex); } static void @@ -913,10 +983,12 @@ HIDAPI_JoystickQuit(void) { int i; + shutting_down = SDL_TRUE; + HIDAPI_ShutdownDiscovery(); while (SDL_HIDAPI_devices) { - HIDAPI_DelDevice(SDL_HIDAPI_devices, SDL_FALSE); + HIDAPI_DelDevice(SDL_HIDAPI_devices); } for (i = 0; i < SDL_arraysize(SDL_HIDAPI_drivers); ++i) { SDL_HIDAPI_DeviceDriver *driver = SDL_HIDAPI_drivers[i]; @@ -924,10 +996,14 @@ HIDAPI_JoystickQuit(void) } SDL_DelHintCallback(SDL_HINT_JOYSTICK_HIDAPI, SDL_HIDAPIDriverHintChanged, NULL); - SDL_HIDAPI_numjoysticks = 0; + SDL_DestroyMutex(SDL_HIDAPI_mutex); hid_exit(); + /* Make sure the drivers cleaned up properly */ + SDL_assert(SDL_HIDAPI_numjoysticks == 0); + + shutting_down = SDL_FALSE; initialized = SDL_FALSE; } diff --git a/src/joystick/hidapi/SDL_hidapijoystick_c.h b/src/joystick/hidapi/SDL_hidapijoystick_c.h index 3acfc4736..36d1b2565 100644 --- a/src/joystick/hidapi/SDL_hidapijoystick_c.h +++ b/src/joystick/hidapi/SDL_hidapijoystick_c.h @@ -46,16 +46,45 @@ /* Prevent rumble duration overflow */ #define SDL_MAX_RUMBLE_DURATION_MS 0x0fffffff +/* Forward declaration */ +struct _SDL_HIDAPI_DeviceDriver; + +typedef struct _SDL_HIDAPI_Device +{ + char *name; + char *path; + Uint16 vendor_id; + Uint16 product_id; + Uint16 version; + SDL_JoystickGUID guid; + int interface_number; /* Available on Windows and Linux */ + Uint16 usage_page; /* Available on Windows and Mac OS X */ + Uint16 usage; /* Available on Windows and Mac OS X */ + + struct _SDL_HIDAPI_DeviceDriver *driver; + void *context; + hid_device *dev; + int num_joysticks; + SDL_JoystickID *joysticks; + + /* Used during scanning for device changes */ + SDL_bool seen; + + struct _SDL_HIDAPI_Device *next; +} SDL_HIDAPI_Device; + 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 *name); 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 (*InitDevice)(SDL_HIDAPI_Device *device); + SDL_bool (*UpdateDevice)(SDL_HIDAPI_Device *device); + SDL_bool (*OpenJoystick)(SDL_HIDAPI_Device *device, SDL_Joystick *joystick); + int (*RumbleJoystick)(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms); + void (*CloseJoystick)(SDL_HIDAPI_Device *device, SDL_Joystick *joystick); + void (*FreeDevice)(SDL_HIDAPI_Device *device); } SDL_HIDAPI_DeviceDriver; @@ -69,6 +98,9 @@ extern SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverXboxOne; /* 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, const char *name); +extern SDL_bool HIDAPI_JoystickConnected(SDL_HIDAPI_Device *device, SDL_JoystickID *pJoystickID); +extern void HIDAPI_JoystickDisconnected(SDL_HIDAPI_Device *device, SDL_JoystickID joystickID); + #endif /* SDL_JOYSTICK_HIDAPI_H */ /* vi: set ts=4 sw=4 expandtab: */