Refactored HIDAPI controller code to support dongles and hubs that dynamically attach controllers
authorSam Lantinga <slouken@libsdl.org>
Thu, 19 Dec 2019 15:01:30 -0800
changeset 133548c22865bd138
parent 13353 381ddcd3957c
child 13355 1adaa20a4ae0
Refactored HIDAPI controller code to support dongles and hubs that dynamically attach controllers
src/joystick/hidapi/SDL_hidapi_ps4.c
src/joystick/hidapi/SDL_hidapi_switch.c
src/joystick/hidapi/SDL_hidapi_xbox360.c
src/joystick/hidapi/SDL_hidapi_xboxone.c
src/joystick/hidapi/SDL_hidapijoystick.c
src/joystick/hidapi/SDL_hidapijoystick_c.h
     1.1 --- a/src/joystick/hidapi/SDL_hidapi_ps4.c	Thu Dec 19 13:54:03 2019 +0100
     1.2 +++ b/src/joystick/hidapi/SDL_hidapi_ps4.c	Thu Dec 19 15:01:30 2019 -0800
     1.3 @@ -193,10 +193,16 @@
     1.4      return SDL_TRUE;
     1.5  }
     1.6  
     1.7 -static int HIDAPI_DriverPS4_Rumble(SDL_Joystick *joystick, hid_device *dev, void *context, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms);
     1.8 +static SDL_bool
     1.9 +HIDAPI_DriverPS4_InitDevice(SDL_HIDAPI_Device *device)
    1.10 +{
    1.11 +    return HIDAPI_JoystickConnected(device, NULL);
    1.12 +}
    1.13 +
    1.14 +static int HIDAPI_DriverPS4_RumbleJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms);
    1.15  
    1.16  static SDL_bool
    1.17 -HIDAPI_DriverPS4_Init(SDL_Joystick *joystick, hid_device *dev, Uint16 vendor_id, Uint16 product_id, void **context)
    1.18 +HIDAPI_DriverPS4_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
    1.19  {
    1.20      SDL_DriverPS4_Context *ctx;
    1.21  
    1.22 @@ -205,14 +211,21 @@
    1.23          SDL_OutOfMemory();
    1.24          return SDL_FALSE;
    1.25      }
    1.26 -    *context = ctx;
    1.27 +
    1.28 +    device->dev = hid_open_path(device->path, 0);
    1.29 +    if (!device->dev) {
    1.30 +        SDL_free(ctx);
    1.31 +        SDL_SetError("Couldn't open %s", device->path);
    1.32 +        return SDL_FALSE;
    1.33 +    }
    1.34 +    device->context = ctx;
    1.35  
    1.36      /* Check for type of connection */
    1.37 -    ctx->is_dongle = (vendor_id == SONY_USB_VID && product_id == SONY_DS4_DONGLE_PID);
    1.38 +    ctx->is_dongle = (device->vendor_id == SONY_USB_VID && device->product_id == SONY_DS4_DONGLE_PID);
    1.39      if (ctx->is_dongle) {
    1.40          ctx->is_bluetooth = SDL_FALSE;
    1.41 -    } else if (vendor_id == SONY_USB_VID) {
    1.42 -        ctx->is_bluetooth = !CheckUSBConnected(dev);
    1.43 +    } else if (device->vendor_id == SONY_USB_VID) {
    1.44 +        ctx->is_bluetooth = !CheckUSBConnected(device->dev);
    1.45      } else {
    1.46          /* Third party controllers appear to all be wired */
    1.47          ctx->is_bluetooth = SDL_FALSE;
    1.48 @@ -222,12 +235,12 @@
    1.49  #endif
    1.50  
    1.51      /* Check to see if audio is supported */
    1.52 -    if (vendor_id == SONY_USB_VID &&
    1.53 -        (product_id == SONY_DS4_SLIM_PID || product_id == SONY_DS4_DONGLE_PID )) {
    1.54 +    if (device->vendor_id == SONY_USB_VID &&
    1.55 +        (device->product_id == SONY_DS4_SLIM_PID || device->product_id == SONY_DS4_DONGLE_PID )) {
    1.56          ctx->audio_supported = SDL_TRUE;
    1.57      }
    1.58  
    1.59 -    if (HIDAPI_DriverPS4_CanRumble(vendor_id, product_id)) {
    1.60 +    if (HIDAPI_DriverPS4_CanRumble(device->vendor_id, device->product_id)) {
    1.61          if (ctx->is_bluetooth) {
    1.62              ctx->rumble_supported = SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI_PS4_RUMBLE, SDL_FALSE);
    1.63          } else {
    1.64 @@ -236,7 +249,7 @@
    1.65      }
    1.66  
    1.67      /* Initialize LED and effect state */
    1.68 -    HIDAPI_DriverPS4_Rumble(joystick, dev, ctx, 0, 0, 0);
    1.69 +    HIDAPI_DriverPS4_RumbleJoystick(device, joystick, 0, 0, 0);
    1.70  
    1.71      /* Initialize the joystick capabilities */
    1.72      joystick->nbuttons = 16;
    1.73 @@ -247,9 +260,9 @@
    1.74  }
    1.75  
    1.76  static int
    1.77 -HIDAPI_DriverPS4_Rumble(SDL_Joystick *joystick, hid_device *dev, void *context, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms)
    1.78 +HIDAPI_DriverPS4_RumbleJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms)
    1.79  {
    1.80 -    SDL_DriverPS4_Context *ctx = (SDL_DriverPS4_Context *)context;
    1.81 +    SDL_DriverPS4_Context *ctx = (SDL_DriverPS4_Context *)device->context;
    1.82      DS4EffectsState_t *effects;
    1.83      Uint8 data[78];
    1.84      int report_size, offset;
    1.85 @@ -293,7 +306,7 @@
    1.86          SDL_memcpy(&data[report_size - sizeof(unCRC)], &unCRC, sizeof(unCRC));
    1.87      }
    1.88  
    1.89 -    if (hid_write(dev, data, report_size) != report_size) {
    1.90 +    if (hid_write(device->dev, data, report_size) != report_size) {
    1.91          return SDL_SetError("Couldn't send rumble packet");
    1.92      }
    1.93  
    1.94 @@ -416,20 +429,28 @@
    1.95  }
    1.96  
    1.97  static SDL_bool
    1.98 -HIDAPI_DriverPS4_Update(SDL_Joystick *joystick, hid_device *dev, void *context)
    1.99 +HIDAPI_DriverPS4_UpdateDevice(SDL_HIDAPI_Device *device)
   1.100  {
   1.101 -    SDL_DriverPS4_Context *ctx = (SDL_DriverPS4_Context *)context;
   1.102 +    SDL_DriverPS4_Context *ctx = (SDL_DriverPS4_Context *)device->context;
   1.103 +    SDL_Joystick *joystick = NULL;
   1.104      Uint8 data[USB_PACKET_LENGTH];
   1.105      int size;
   1.106  
   1.107 -    while ((size = hid_read_timeout(dev, data, sizeof(data), 0)) > 0) {
   1.108 +    if (device->num_joysticks > 0) {
   1.109 +        joystick = SDL_JoystickFromInstanceID(device->joysticks[0]);
   1.110 +    }
   1.111 +    if (!joystick) {
   1.112 +        return SDL_FALSE;
   1.113 +    }
   1.114 +
   1.115 +    while ((size = hid_read_timeout(device->dev, data, sizeof(data), 0)) > 0) {
   1.116          switch (data[0]) {
   1.117          case k_EPS4ReportIdUsbState:
   1.118 -            HIDAPI_DriverPS4_HandleStatePacket(joystick, dev, ctx, (PS4StatePacket_t *)&data[1]);
   1.119 +            HIDAPI_DriverPS4_HandleStatePacket(joystick, device->dev, ctx, (PS4StatePacket_t *)&data[1]);
   1.120              break;
   1.121          case k_EPS4ReportIdBluetoothState:
   1.122              /* Bluetooth state packets have two additional bytes at the beginning */
   1.123 -            HIDAPI_DriverPS4_HandleStatePacket(joystick, dev, ctx, (PS4StatePacket_t *)&data[3]);
   1.124 +            HIDAPI_DriverPS4_HandleStatePacket(joystick, device->dev, ctx, (PS4StatePacket_t *)&data[3]);
   1.125              break;
   1.126          default:
   1.127  #ifdef DEBUG_JOYSTICK
   1.128 @@ -442,17 +463,30 @@
   1.129      if (ctx->rumble_expiration) {
   1.130          Uint32 now = SDL_GetTicks();
   1.131          if (SDL_TICKS_PASSED(now, ctx->rumble_expiration)) {
   1.132 -            HIDAPI_DriverPS4_Rumble(joystick, dev, context, 0, 0, 0);
   1.133 +            HIDAPI_DriverPS4_RumbleJoystick(device, joystick, 0, 0, 0);
   1.134          }
   1.135      }
   1.136  
   1.137 +    if (size < 0) {
   1.138 +        /* Read error, device is disconnected */
   1.139 +        HIDAPI_JoystickDisconnected(device, joystick->instance_id);
   1.140 +    }
   1.141      return (size >= 0);
   1.142  }
   1.143  
   1.144  static void
   1.145 -HIDAPI_DriverPS4_Quit(SDL_Joystick *joystick, hid_device *dev, void *context)
   1.146 +HIDAPI_DriverPS4_CloseJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
   1.147  {
   1.148 -    SDL_free(context);
   1.149 +    hid_close(device->dev);
   1.150 +    device->dev = NULL;
   1.151 +
   1.152 +    SDL_free(device->context);
   1.153 +    device->context = NULL;
   1.154 +}
   1.155 +
   1.156 +static void
   1.157 +HIDAPI_DriverPS4_FreeDevice(SDL_HIDAPI_Device *device)
   1.158 +{
   1.159  }
   1.160  
   1.161  SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverPS4 =
   1.162 @@ -461,10 +495,12 @@
   1.163      SDL_TRUE,
   1.164      HIDAPI_DriverPS4_IsSupportedDevice,
   1.165      HIDAPI_DriverPS4_GetDeviceName,
   1.166 -    HIDAPI_DriverPS4_Init,
   1.167 -    HIDAPI_DriverPS4_Rumble,
   1.168 -    HIDAPI_DriverPS4_Update,
   1.169 -    HIDAPI_DriverPS4_Quit
   1.170 +    HIDAPI_DriverPS4_InitDevice,
   1.171 +    HIDAPI_DriverPS4_UpdateDevice,
   1.172 +    HIDAPI_DriverPS4_OpenJoystick,
   1.173 +    HIDAPI_DriverPS4_RumbleJoystick,
   1.174 +    HIDAPI_DriverPS4_CloseJoystick,
   1.175 +    HIDAPI_DriverPS4_FreeDevice
   1.176  };
   1.177  
   1.178  #endif /* SDL_JOYSTICK_HIDAPI_PS4 */
     2.1 --- a/src/joystick/hidapi/SDL_hidapi_switch.c	Thu Dec 19 13:54:03 2019 +0100
     2.2 +++ b/src/joystick/hidapi/SDL_hidapi_switch.c	Thu Dec 19 15:01:30 2019 -0800
     2.3 @@ -223,6 +223,23 @@
     2.4  } SDL_DriverSwitch_Context;
     2.5  
     2.6  
     2.7 +static SDL_bool IsGameCubeFormFactor(int vendor_id, int product_id)
     2.8 +{
     2.9 +    static Uint32 gamecube_formfactor[] = {
    2.10 +        MAKE_VIDPID(0x0e6f, 0x0185),    /* PDP Wired Fight Pad Pro for Nintendo Switch */
    2.11 +        MAKE_VIDPID(0x20d6, 0xa711),    /* Core (Plus) Wired Controller */
    2.12 +    };
    2.13 +    Uint32 id = MAKE_VIDPID(vendor_id, product_id);
    2.14 +    int i;
    2.15 +
    2.16 +    for (i = 0; i < SDL_arraysize(gamecube_formfactor); ++i) {
    2.17 +        if (id == gamecube_formfactor[i]) {
    2.18 +            return SDL_TRUE;
    2.19 +        }
    2.20 +    }
    2.21 +    return SDL_FALSE;
    2.22 +}
    2.23 +
    2.24  static SDL_bool
    2.25  HIDAPI_DriverSwitch_IsSupportedDevice(Uint16 vendor_id, Uint16 product_id, Uint16 version, int interface_number, const char *name)
    2.26  {
    2.27 @@ -604,9 +621,15 @@
    2.28      }
    2.29      return button;
    2.30  }
    2.31 -        
    2.32 + 
    2.33  static SDL_bool
    2.34 -HIDAPI_DriverSwitch_Init(SDL_Joystick *joystick, hid_device *dev, Uint16 vendor_id, Uint16 product_id, void **context)
    2.35 +HIDAPI_DriverSwitch_InitDevice(SDL_HIDAPI_Device *device)
    2.36 +{
    2.37 +    return HIDAPI_JoystickConnected(device, NULL);
    2.38 +}
    2.39 +
    2.40 +static SDL_bool
    2.41 +HIDAPI_DriverSwitch_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
    2.42  {
    2.43      SDL_DriverSwitch_Context *ctx;
    2.44      Uint8 input_mode;
    2.45 @@ -616,15 +639,19 @@
    2.46          SDL_OutOfMemory();
    2.47          return SDL_FALSE;
    2.48      }
    2.49 -    ctx->dev = dev;
    2.50  
    2.51 -    *context = ctx;
    2.52 +    device->dev = ctx->dev = hid_open_path(device->path, 0);
    2.53 +    if (!device->dev) {
    2.54 +        SDL_SetError("Couldn't open %s", device->path);
    2.55 +        goto error;
    2.56 +    }
    2.57 +    device->context = ctx;
    2.58  
    2.59      /* Find out whether or not we can send output reports */
    2.60 -    ctx->m_bInputOnly = SDL_IsJoystickNintendoSwitchProInputOnly(vendor_id, product_id);
    2.61 +    ctx->m_bInputOnly = SDL_IsJoystickNintendoSwitchProInputOnly(device->vendor_id, device->product_id);
    2.62      if (!ctx->m_bInputOnly) {
    2.63          /* The Power A Nintendo Switch Pro controllers don't have a Home LED */
    2.64 -        ctx->m_bHasHomeLED = (vendor_id != 0 && product_id != 0) ? SDL_TRUE : SDL_FALSE;
    2.65 +        ctx->m_bHasHomeLED = (device->vendor_id != 0 && device->product_id != 0) ? SDL_TRUE : SDL_FALSE;
    2.66  
    2.67          /* Initialize rumble data */
    2.68          SetNeutralRumble(&ctx->m_RumblePacket.rumbleData[0]);
    2.69 @@ -637,14 +664,12 @@
    2.70  
    2.71          if (!LoadStickCalibration(ctx)) {
    2.72              SDL_SetError("Couldn't load stick calibration");
    2.73 -            SDL_free(ctx);
    2.74 -            return SDL_FALSE;
    2.75 +            goto error;
    2.76          }
    2.77  
    2.78          if (!SetVibrationEnabled(ctx, 1)) {
    2.79              SDL_SetError("Couldn't enable vibration");
    2.80 -            SDL_free(ctx);
    2.81 -            return SDL_FALSE;
    2.82 +            goto error;
    2.83          }
    2.84  
    2.85          /* Set the desired input mode */
    2.86 @@ -655,8 +680,7 @@
    2.87          }
    2.88          if (!SetInputMode(ctx, input_mode)) {
    2.89              SDL_SetError("Couldn't set input mode");
    2.90 -            SDL_free(ctx);
    2.91 -            return SDL_FALSE;
    2.92 +            goto error;
    2.93          }
    2.94  
    2.95          /* Start sending USB reports */
    2.96 @@ -664,8 +688,7 @@
    2.97              /* ForceUSB doesn't generate an ACK, so don't wait for a reply */
    2.98              if (!WriteProprietary(ctx, k_eSwitchProprietaryCommandIDs_ForceUSB, NULL, 0, SDL_FALSE)) {
    2.99                  SDL_SetError("Couldn't start USB reports");
   2.100 -                SDL_free(ctx);
   2.101 -                return SDL_FALSE;
   2.102 +                goto error;
   2.103              }
   2.104          }
   2.105  
   2.106 @@ -676,7 +699,7 @@
   2.107          SetSlotLED(ctx, (joystick->instance_id % 4));
   2.108      }
   2.109  
   2.110 -    if (vendor_id == 0x0e6f && product_id == 0x0185) {
   2.111 +    if (IsGameCubeFormFactor(device->vendor_id, device->product_id)) {
   2.112          /* This is a controller shaped like a GameCube controller, with a large central A button */
   2.113          ctx->m_bUseButtonLabels = SDL_TRUE;
   2.114      } else {
   2.115 @@ -690,12 +713,23 @@
   2.116      joystick->epowerlevel = SDL_JOYSTICK_POWER_WIRED;
   2.117  
   2.118      return SDL_TRUE;
   2.119 +
   2.120 +error:
   2.121 +    if (device->dev) {
   2.122 +        hid_close(device->dev);
   2.123 +        device->dev = NULL;
   2.124 +    }
   2.125 +    if (device->context) {
   2.126 +        SDL_free(device->context);
   2.127 +        device->context = NULL;
   2.128 +    }
   2.129 +    return SDL_FALSE;
   2.130  }
   2.131  
   2.132  static int
   2.133 -HIDAPI_DriverSwitch_Rumble(SDL_Joystick *joystick, hid_device *dev, void *context, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms)
   2.134 +HIDAPI_DriverSwitch_RumbleJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms)
   2.135  {
   2.136 -    SDL_DriverSwitch_Context *ctx = (SDL_DriverSwitch_Context *)context;
   2.137 +    SDL_DriverSwitch_Context *ctx = (SDL_DriverSwitch_Context *)device->context;
   2.138  
   2.139      /* Experimentally determined rumble values. These will only matter on some controllers as tested ones
   2.140       * seem to disregard these and just use any non-zero rumble values as a binary flag for constant rumble
   2.141 @@ -996,11 +1030,19 @@
   2.142  }
   2.143  
   2.144  static SDL_bool
   2.145 -HIDAPI_DriverSwitch_Update(SDL_Joystick *joystick, hid_device *dev, void *context)
   2.146 +HIDAPI_DriverSwitch_UpdateDevice(SDL_HIDAPI_Device *device)
   2.147  {
   2.148 -    SDL_DriverSwitch_Context *ctx = (SDL_DriverSwitch_Context *)context;
   2.149 +    SDL_DriverSwitch_Context *ctx = (SDL_DriverSwitch_Context *)device->context;
   2.150 +    SDL_Joystick *joystick = NULL;
   2.151      int size;
   2.152  
   2.153 +    if (device->num_joysticks > 0) {
   2.154 +        joystick = SDL_JoystickFromInstanceID(device->joysticks[0]);
   2.155 +    }
   2.156 +    if (!joystick) {
   2.157 +        return SDL_FALSE;
   2.158 +    }
   2.159 +
   2.160      while ((size = ReadInput(ctx)) > 0) {
   2.161          if (ctx->m_bInputOnly) {
   2.162              HandleInputOnlyControllerState(joystick, ctx, (SwitchInputOnlyControllerStatePacket_t *)&ctx->m_rgucReadBuffer[0]);
   2.163 @@ -1021,17 +1063,21 @@
   2.164      if (ctx->m_nRumbleExpiration) {
   2.165          Uint32 now = SDL_GetTicks();
   2.166          if (SDL_TICKS_PASSED(now, ctx->m_nRumbleExpiration)) {
   2.167 -            HIDAPI_DriverSwitch_Rumble(joystick, dev, context, 0, 0, 0);
   2.168 +            HIDAPI_DriverSwitch_RumbleJoystick(device, joystick, 0, 0, 0);
   2.169          }
   2.170      }
   2.171  
   2.172 +    if (size < 0) {
   2.173 +        /* Read error, device is disconnected */
   2.174 +        HIDAPI_JoystickDisconnected(device, joystick->instance_id);
   2.175 +    }
   2.176      return (size >= 0);
   2.177  }
   2.178  
   2.179  static void
   2.180 -HIDAPI_DriverSwitch_Quit(SDL_Joystick *joystick, hid_device *dev, void *context)
   2.181 +HIDAPI_DriverSwitch_CloseJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
   2.182  {
   2.183 -    SDL_DriverSwitch_Context *ctx = (SDL_DriverSwitch_Context *)context;
   2.184 +    SDL_DriverSwitch_Context *ctx = (SDL_DriverSwitch_Context *)device->context;
   2.185  
   2.186      if (!ctx->m_bInputOnly) {
   2.187          /* Restore simple input mode for other applications */
   2.188 @@ -1041,7 +1087,16 @@
   2.189      SDL_DelHintCallback(SDL_HINT_GAMECONTROLLER_USE_BUTTON_LABELS,
   2.190                          SDL_GameControllerButtonReportingHintChanged, ctx);
   2.191  
   2.192 -    SDL_free(context);
   2.193 +    hid_close(device->dev);
   2.194 +    device->dev = NULL;
   2.195 +
   2.196 +    SDL_free(device->context);
   2.197 +    device->context = NULL;
   2.198 +}
   2.199 +
   2.200 +static void
   2.201 +HIDAPI_DriverSwitch_FreeDevice(SDL_HIDAPI_Device *device)
   2.202 +{
   2.203  }
   2.204  
   2.205  SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverSwitch =
   2.206 @@ -1050,10 +1105,12 @@
   2.207      SDL_TRUE,
   2.208      HIDAPI_DriverSwitch_IsSupportedDevice,
   2.209      HIDAPI_DriverSwitch_GetDeviceName,
   2.210 -    HIDAPI_DriverSwitch_Init,
   2.211 -    HIDAPI_DriverSwitch_Rumble,
   2.212 -    HIDAPI_DriverSwitch_Update,
   2.213 -    HIDAPI_DriverSwitch_Quit
   2.214 +    HIDAPI_DriverSwitch_InitDevice,
   2.215 +    HIDAPI_DriverSwitch_UpdateDevice,
   2.216 +    HIDAPI_DriverSwitch_OpenJoystick,
   2.217 +    HIDAPI_DriverSwitch_RumbleJoystick,
   2.218 +    HIDAPI_DriverSwitch_CloseJoystick,
   2.219 +    HIDAPI_DriverSwitch_FreeDevice
   2.220  };
   2.221  
   2.222  #endif /* SDL_JOYSTICK_HIDAPI_SWITCH */
     3.1 --- a/src/joystick/hidapi/SDL_hidapi_xbox360.c	Thu Dec 19 13:54:03 2019 +0100
     3.2 +++ b/src/joystick/hidapi/SDL_hidapi_xbox360.c	Thu Dec 19 15:01:30 2019 -0800
     3.3 @@ -291,7 +291,13 @@
     3.4  }
     3.5  
     3.6  static SDL_bool
     3.7 -HIDAPI_DriverXbox360_Init(SDL_Joystick *joystick, hid_device *dev, Uint16 vendor_id, Uint16 product_id, void **context)
     3.8 +HIDAPI_DriverXbox360_InitDevice(SDL_HIDAPI_Device *device)
     3.9 +{
    3.10 +    return HIDAPI_JoystickConnected(device, NULL);
    3.11 +}
    3.12 +
    3.13 +static SDL_bool
    3.14 +HIDAPI_DriverXbox360_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
    3.15  {
    3.16      SDL_DriverXbox360_Context *ctx;
    3.17  
    3.18 @@ -300,6 +306,15 @@
    3.19          SDL_OutOfMemory();
    3.20          return SDL_FALSE;
    3.21      }
    3.22 +
    3.23 +    device->dev = hid_open_path(device->path, 0);
    3.24 +    if (!device->dev) {
    3.25 +        SDL_free(ctx);
    3.26 +        SDL_SetError("Couldn't open %s", device->path);
    3.27 +        return SDL_FALSE;
    3.28 +    }
    3.29 +    device->context = ctx;
    3.30 +
    3.31  #ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT
    3.32      ctx->xinput_enabled = SDL_GetHintBoolean(SDL_HINT_XINPUT_ENABLED, SDL_TRUE);
    3.33      if (ctx->xinput_enabled && WIN_LoadXInputDLL() < 0) {
    3.34 @@ -310,10 +325,9 @@
    3.35  #ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT
    3.36      HIDAPI_DriverXbox360_InitWindowsGamingInput(ctx);
    3.37  #endif
    3.38 -    *context = ctx;
    3.39  
    3.40      /* Set the controller LED */
    3.41 -    SetSlotLED(dev, (joystick->instance_id % 4));
    3.42 +    SetSlotLED(device->dev, (joystick->instance_id % 4));
    3.43  
    3.44      /* Initialize the joystick capabilities */
    3.45      joystick->nbuttons = SDL_CONTROLLER_BUTTON_MAX;
    3.46 @@ -324,9 +338,9 @@
    3.47  }
    3.48  
    3.49  static int
    3.50 -HIDAPI_DriverXbox360_Rumble(SDL_Joystick *joystick, hid_device *dev, void *context, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms)
    3.51 +HIDAPI_DriverXbox360_RumbleJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms)
    3.52  {
    3.53 -    SDL_DriverXbox360_Context *ctx = (SDL_DriverXbox360_Context *)context;
    3.54 +    SDL_DriverXbox360_Context *ctx = (SDL_DriverXbox360_Context *)device->context;
    3.55  
    3.56  #ifdef __WIN32__
    3.57      SDL_bool rumbled = SDL_FALSE;
    3.58 @@ -379,7 +393,7 @@
    3.59      rumble_packet[4] = (high_frequency_rumble >> 8);
    3.60  #endif
    3.61  
    3.62 -    if (hid_write(dev, rumble_packet, sizeof(rumble_packet)) != sizeof(rumble_packet)) {
    3.63 +    if (hid_write(device->dev, rumble_packet, sizeof(rumble_packet)) != sizeof(rumble_packet)) {
    3.64          return SDL_SetError("Couldn't send rumble packet");
    3.65      }
    3.66  #endif /* __WIN32__ */
    3.67 @@ -719,26 +733,34 @@
    3.68  #endif /* __MACOSX__ */
    3.69  
    3.70  static SDL_bool
    3.71 -HIDAPI_DriverXbox360_Update(SDL_Joystick *joystick, hid_device *dev, void *context)
    3.72 +HIDAPI_DriverXbox360_UpdateDevice(SDL_HIDAPI_Device *device)
    3.73  {
    3.74 -    SDL_DriverXbox360_Context *ctx = (SDL_DriverXbox360_Context *)context;
    3.75 +    SDL_DriverXbox360_Context *ctx = (SDL_DriverXbox360_Context *)device->context;
    3.76 +    SDL_Joystick *joystick = NULL;
    3.77      Uint8 data[USB_PACKET_LENGTH];
    3.78      int size;
    3.79  
    3.80 -    while ((size = hid_read_timeout(dev, data, sizeof(data), 0)) > 0) {
    3.81 +    if (device->num_joysticks > 0) {
    3.82 +        joystick = SDL_JoystickFromInstanceID(device->joysticks[0]);
    3.83 +    }
    3.84 +    if (!joystick) {
    3.85 +        return SDL_FALSE;
    3.86 +    }
    3.87 +
    3.88 +    while ((size = hid_read_timeout(device->dev, data, sizeof(data), 0)) > 0) {
    3.89  #ifdef __WIN32__
    3.90 -        HIDAPI_DriverXbox360_HandleStatePacket(joystick, dev, ctx, data, size);
    3.91 +        HIDAPI_DriverXbox360_HandleStatePacket(joystick, device->dev, ctx, data, size);
    3.92  #else
    3.93          switch (data[0]) {
    3.94          case 0x00:
    3.95 -            HIDAPI_DriverXbox360_HandleStatePacket(joystick, dev, ctx, data, size);
    3.96 +            HIDAPI_DriverXbox360_HandleStatePacket(joystick, device->dev, ctx, data, size);
    3.97              break;
    3.98  #ifdef __MACOSX__
    3.99          case 0x01:
   3.100 -            HIDAPI_DriverXboxOneS_HandleStatePacket(joystick, dev, ctx, data, size);
   3.101 +            HIDAPI_DriverXboxOneS_HandleStatePacket(joystick, device->dev, ctx, data, size);
   3.102              break;
   3.103          case 0x02:
   3.104 -            HIDAPI_DriverXboxOneS_HandleGuidePacket(joystick, dev, ctx, data, size);
   3.105 +            HIDAPI_DriverXboxOneS_HandleGuidePacket(joystick, device->dev, ctx, data, size);
   3.106              break;
   3.107  #endif
   3.108          default:
   3.109 @@ -756,18 +778,22 @@
   3.110      if (ctx->rumble_expiration) {
   3.111          Uint32 now = SDL_GetTicks();
   3.112          if (SDL_TICKS_PASSED(now, ctx->rumble_expiration)) {
   3.113 -            HIDAPI_DriverXbox360_Rumble(joystick, dev, context, 0, 0, 0);
   3.114 +            HIDAPI_DriverXbox360_RumbleJoystick(device, joystick, 0, 0, 0);
   3.115          }
   3.116      }
   3.117  
   3.118 +    if (size < 0) {
   3.119 +        /* Read error, device is disconnected */
   3.120 +        HIDAPI_JoystickDisconnected(device, joystick->instance_id);
   3.121 +    }
   3.122      return (size >= 0);
   3.123  }
   3.124  
   3.125  static void
   3.126 -HIDAPI_DriverXbox360_Quit(SDL_Joystick *joystick, hid_device *dev, void *context)
   3.127 +HIDAPI_DriverXbox360_CloseJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
   3.128  {
   3.129  #if defined(SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT) || defined(SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT)
   3.130 -    SDL_DriverXbox360_Context *ctx = (SDL_DriverXbox360_Context *)context;
   3.131 +    SDL_DriverXbox360_Context *ctx = (SDL_DriverXbox360_Context *)device->context;
   3.132  #endif
   3.133  
   3.134  #ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_XINPUT
   3.135 @@ -777,9 +803,19 @@
   3.136      }
   3.137  #endif
   3.138  #ifdef SDL_JOYSTICK_HIDAPI_WINDOWS_GAMING_INPUT
   3.139 -    HIDAPI_DriverXbox360_InitWindowsGamingInput(ctx);
   3.140 +    HIDAPI_DriverXbox360_QuitWindowsGamingInput(ctx);
   3.141  #endif
   3.142 -    SDL_free(context);
   3.143 +
   3.144 +    hid_close(device->dev);
   3.145 +    device->dev = NULL;
   3.146 +
   3.147 +    SDL_free(device->context);
   3.148 +    device->context = NULL;
   3.149 +}
   3.150 +
   3.151 +static void
   3.152 +HIDAPI_DriverXbox360_FreeDevice(SDL_HIDAPI_Device *device)
   3.153 +{
   3.154  }
   3.155  
   3.156  SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverXbox360 =
   3.157 @@ -788,10 +824,12 @@
   3.158      SDL_TRUE,
   3.159      HIDAPI_DriverXbox360_IsSupportedDevice,
   3.160      HIDAPI_DriverXbox360_GetDeviceName,
   3.161 -    HIDAPI_DriverXbox360_Init,
   3.162 -    HIDAPI_DriverXbox360_Rumble,
   3.163 -    HIDAPI_DriverXbox360_Update,
   3.164 -    HIDAPI_DriverXbox360_Quit
   3.165 +    HIDAPI_DriverXbox360_InitDevice,
   3.166 +    HIDAPI_DriverXbox360_UpdateDevice,
   3.167 +    HIDAPI_DriverXbox360_OpenJoystick,
   3.168 +    HIDAPI_DriverXbox360_RumbleJoystick,
   3.169 +    HIDAPI_DriverXbox360_CloseJoystick,
   3.170 +    HIDAPI_DriverXbox360_FreeDevice
   3.171  };
   3.172  
   3.173  #endif /* SDL_JOYSTICK_HIDAPI_XBOX360 */
     4.1 --- a/src/joystick/hidapi/SDL_hidapi_xboxone.c	Thu Dec 19 13:54:03 2019 +0100
     4.2 +++ b/src/joystick/hidapi/SDL_hidapi_xboxone.c	Thu Dec 19 15:01:30 2019 -0800
     4.3 @@ -255,7 +255,13 @@
     4.4  }
     4.5  
     4.6  static SDL_bool
     4.7 -HIDAPI_DriverXboxOne_Init(SDL_Joystick *joystick, hid_device *dev, Uint16 vendor_id, Uint16 product_id, void **context)
     4.8 +HIDAPI_DriverXboxOne_InitDevice(SDL_HIDAPI_Device *device)
     4.9 +{
    4.10 +    return HIDAPI_JoystickConnected(device, NULL);
    4.11 +}
    4.12 +
    4.13 +static SDL_bool
    4.14 +HIDAPI_DriverXboxOne_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
    4.15  {
    4.16      SDL_DriverXboxOne_Context *ctx;
    4.17  
    4.18 @@ -264,10 +270,17 @@
    4.19          SDL_OutOfMemory();
    4.20          return SDL_FALSE;
    4.21      }
    4.22 -    *context = ctx;
    4.23  
    4.24 -    ctx->vendor_id = vendor_id;
    4.25 -    ctx->product_id = product_id;
    4.26 +    device->dev = hid_open_path(device->path, 0);
    4.27 +    if (!device->dev) {
    4.28 +        SDL_free(ctx);
    4.29 +        SDL_SetError("Couldn't open %s", device->path);
    4.30 +        return SDL_FALSE;
    4.31 +    }
    4.32 +    device->context = ctx;
    4.33 +
    4.34 +    ctx->vendor_id = device->vendor_id;
    4.35 +    ctx->product_id = device->product_id;
    4.36      ctx->start_time = SDL_GetTicks();
    4.37  
    4.38      /* Initialize the joystick capabilities */
    4.39 @@ -279,9 +292,9 @@
    4.40  }
    4.41  
    4.42  static int
    4.43 -HIDAPI_DriverXboxOne_Rumble(SDL_Joystick *joystick, hid_device *dev, void *context, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms)
    4.44 +HIDAPI_DriverXboxOne_RumbleJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms)
    4.45  {
    4.46 -    SDL_DriverXboxOne_Context *ctx = (SDL_DriverXboxOne_Context *)context;
    4.47 +    SDL_DriverXboxOne_Context *ctx = (SDL_DriverXboxOne_Context *)device->context;
    4.48      Uint8 rumble_packet[] = { 0x09, 0x00, 0x00, 0x09, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF };
    4.49  
    4.50      if (!ctx->initialized) {
    4.51 @@ -293,7 +306,7 @@
    4.52      rumble_packet[8] = low_frequency_rumble / 655;
    4.53      rumble_packet[9] = high_frequency_rumble / 655;
    4.54  
    4.55 -    if (hid_write(dev, rumble_packet, sizeof(rumble_packet)) != sizeof(rumble_packet)) {
    4.56 +    if (hid_write(device->dev, rumble_packet, sizeof(rumble_packet)) != sizeof(rumble_packet)) {
    4.57          return SDL_SetError("Couldn't send rumble packet");
    4.58      }
    4.59  
    4.60 @@ -366,22 +379,31 @@
    4.61  }
    4.62  
    4.63  static SDL_bool
    4.64 -HIDAPI_DriverXboxOne_Update(SDL_Joystick *joystick, hid_device *dev, void *context)
    4.65 +HIDAPI_DriverXboxOne_UpdateDevice(SDL_HIDAPI_Device *device)
    4.66  {
    4.67 -    SDL_DriverXboxOne_Context *ctx = (SDL_DriverXboxOne_Context *)context;
    4.68 +    SDL_DriverXboxOne_Context *ctx = (SDL_DriverXboxOne_Context *)device->context;
    4.69 +    SDL_Joystick *joystick = NULL;
    4.70      Uint8 data[USB_PACKET_LENGTH];
    4.71      int size;
    4.72  
    4.73 +    if (device->num_joysticks > 0) {
    4.74 +        joystick = SDL_JoystickFromInstanceID(device->joysticks[0]);
    4.75 +    }
    4.76 +    if (!joystick) {
    4.77 +        return SDL_FALSE;
    4.78 +    }
    4.79 +
    4.80      if (!ctx->initialized) {
    4.81          if (SDL_TICKS_PASSED(SDL_GetTicks(), ctx->start_time + CONTROLLER_INIT_DELAY_MS)) {
    4.82 -            if (!SendControllerInit(dev, ctx)) {
    4.83 +            if (!SendControllerInit(device->dev, ctx)) {
    4.84 +                HIDAPI_JoystickDisconnected(device, joystick->instance_id);
    4.85                  return SDL_FALSE;
    4.86              }
    4.87              ctx->initialized = SDL_TRUE;
    4.88          }
    4.89      }
    4.90  
    4.91 -    while ((size = hid_read_timeout(dev, data, sizeof(data), 0)) > 0) {
    4.92 +    while ((size = hid_read_timeout(device->dev, data, sizeof(data), 0)) > 0) {
    4.93  #ifdef DEBUG_XBOX_PROTOCOL
    4.94          SDL_Log("Xbox One packet: size = %d\n"
    4.95                  "                 0x%.2x 0x%.2x 0x%.2x 0x%.2x 0x%.2x 0x%.2x 0x%.2x 0x%.2x\n"
    4.96 @@ -394,10 +416,10 @@
    4.97  #endif
    4.98          switch (data[0]) {
    4.99          case 0x20:
   4.100 -            HIDAPI_DriverXboxOne_HandleStatePacket(joystick, dev, ctx, data, size);
   4.101 +            HIDAPI_DriverXboxOne_HandleStatePacket(joystick, device->dev, ctx, data, size);
   4.102              break;
   4.103          case 0x07:
   4.104 -            HIDAPI_DriverXboxOne_HandleModePacket(joystick, dev, ctx, data, size);
   4.105 +            HIDAPI_DriverXboxOne_HandleModePacket(joystick, device->dev, ctx, data, size);
   4.106              break;
   4.107          default:
   4.108  #ifdef DEBUG_JOYSTICK
   4.109 @@ -410,17 +432,30 @@
   4.110      if (ctx->rumble_expiration) {
   4.111          Uint32 now = SDL_GetTicks();
   4.112          if (SDL_TICKS_PASSED(now, ctx->rumble_expiration)) {
   4.113 -            HIDAPI_DriverXboxOne_Rumble(joystick, dev, context, 0, 0, 0);
   4.114 +            HIDAPI_DriverXboxOne_RumbleJoystick(device, joystick, 0, 0, 0);
   4.115          }
   4.116      }
   4.117  
   4.118 +    if (size < 0) {
   4.119 +        /* Read error, device is disconnected */
   4.120 +        HIDAPI_JoystickDisconnected(device, joystick->instance_id);
   4.121 +    }
   4.122      return (size >= 0);
   4.123  }
   4.124  
   4.125  static void
   4.126 -HIDAPI_DriverXboxOne_Quit(SDL_Joystick *joystick, hid_device *dev, void *context)
   4.127 +HIDAPI_DriverXboxOne_CloseJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
   4.128  {
   4.129 -    SDL_free(context);
   4.130 +    hid_close(device->dev);
   4.131 +    device->dev = NULL;
   4.132 +
   4.133 +    SDL_free(device->context);
   4.134 +    device->context = NULL;
   4.135 +}
   4.136 +
   4.137 +static void
   4.138 +HIDAPI_DriverXboxOne_FreeDevice(SDL_HIDAPI_Device *device)
   4.139 +{
   4.140  }
   4.141  
   4.142  SDL_HIDAPI_DeviceDriver SDL_HIDAPI_DriverXboxOne =
   4.143 @@ -429,10 +464,12 @@
   4.144      SDL_TRUE,
   4.145      HIDAPI_DriverXboxOne_IsSupportedDevice,
   4.146      HIDAPI_DriverXboxOne_GetDeviceName,
   4.147 -    HIDAPI_DriverXboxOne_Init,
   4.148 -    HIDAPI_DriverXboxOne_Rumble,
   4.149 -    HIDAPI_DriverXboxOne_Update,
   4.150 -    HIDAPI_DriverXboxOne_Quit
   4.151 +    HIDAPI_DriverXboxOne_InitDevice,
   4.152 +    HIDAPI_DriverXboxOne_UpdateDevice,
   4.153 +    HIDAPI_DriverXboxOne_OpenJoystick,
   4.154 +    HIDAPI_DriverXboxOne_RumbleJoystick,
   4.155 +    HIDAPI_DriverXboxOne_CloseJoystick,
   4.156 +    HIDAPI_DriverXboxOne_FreeDevice
   4.157  };
   4.158  
   4.159  #endif /* SDL_JOYSTICK_HIDAPI_XBOXONE */
     5.1 --- a/src/joystick/hidapi/SDL_hidapijoystick.c	Thu Dec 19 13:54:03 2019 +0100
     5.2 +++ b/src/joystick/hidapi/SDL_hidapijoystick.c	Thu Dec 19 15:01:30 2019 -0800
     5.3 @@ -22,6 +22,7 @@
     5.4  
     5.5  #ifdef SDL_JOYSTICK_HIDAPI
     5.6  
     5.7 +#include "SDL_assert.h"
     5.8  #include "SDL_endian.h"
     5.9  #include "SDL_hints.h"
    5.10  #include "SDL_log.h"
    5.11 @@ -53,33 +54,9 @@
    5.12  
    5.13  struct joystick_hwdata
    5.14  {
    5.15 -    SDL_HIDAPI_DeviceDriver *driver;
    5.16 -    void *context;
    5.17 -
    5.18 -    SDL_mutex *mutex;
    5.19 -    hid_device *dev;
    5.20 +    SDL_HIDAPI_Device *device;
    5.21  };
    5.22  
    5.23 -typedef struct _SDL_HIDAPI_Device
    5.24 -{
    5.25 -    SDL_JoystickID instance_id;
    5.26 -    char *name;
    5.27 -    char *path;
    5.28 -    Uint16 vendor_id;
    5.29 -    Uint16 product_id;
    5.30 -    Uint16 version;
    5.31 -    SDL_JoystickGUID guid;
    5.32 -    int interface_number;   /* Available on Windows and Linux */
    5.33 -    Uint16 usage_page;      /* Available on Windows and Mac OS X */
    5.34 -    Uint16 usage;           /* Available on Windows and Mac OS X */
    5.35 -    SDL_HIDAPI_DeviceDriver *driver;
    5.36 -
    5.37 -    /* Used during scanning for device changes */
    5.38 -    SDL_bool seen;
    5.39 -
    5.40 -    struct _SDL_HIDAPI_Device *next;
    5.41 -} SDL_HIDAPI_Device;
    5.42 -
    5.43  static SDL_HIDAPI_DeviceDriver *SDL_HIDAPI_drivers[] = {
    5.44  #ifdef SDL_JOYSTICK_HIDAPI_PS4
    5.45      &SDL_HIDAPI_DriverPS4,
    5.46 @@ -98,9 +75,11 @@
    5.47  #endif
    5.48  };
    5.49  static int SDL_HIDAPI_numdrivers = 0;
    5.50 +static SDL_mutex *SDL_HIDAPI_mutex;
    5.51  static SDL_HIDAPI_Device *SDL_HIDAPI_devices;
    5.52  static int SDL_HIDAPI_numjoysticks = 0;
    5.53  static SDL_bool initialized = SDL_FALSE;
    5.54 +static SDL_bool shutting_down = SDL_FALSE;
    5.55  
    5.56  #if defined(SDL_USE_LIBUDEV)
    5.57  static const SDL_UDEV_Symbols * usyms = NULL;
    5.58 @@ -396,6 +375,9 @@
    5.59  #endif
    5.60  }
    5.61  
    5.62 +static void HIDAPI_JoystickDetect(void);
    5.63 +static void HIDAPI_JoystickClose(SDL_Joystick * joystick);
    5.64 +
    5.65  static SDL_bool
    5.66  HIDAPI_IsDeviceSupported(Uint16 vendor_id, Uint16 product_id, Uint16 version, const char *name)
    5.67  {
    5.68 @@ -440,19 +422,22 @@
    5.69  }
    5.70  
    5.71  static SDL_HIDAPI_Device *
    5.72 -HIDAPI_GetJoystickByIndex(int device_index)
    5.73 +HIDAPI_GetDeviceByIndex(int device_index, SDL_JoystickID *pJoystickID)
    5.74  {
    5.75      SDL_HIDAPI_Device *device = SDL_HIDAPI_devices;
    5.76      while (device) {
    5.77          if (device->driver) {
    5.78 -            if (device_index == 0) {
    5.79 -                break;
    5.80 +            if (device_index < device->num_joysticks) {
    5.81 +                if (pJoystickID) {
    5.82 +                    *pJoystickID = device->joysticks[device_index];
    5.83 +                }
    5.84 +                return device;
    5.85              }
    5.86 -            --device_index;
    5.87 +            device_index -= device->num_joysticks;
    5.88          }
    5.89          device = device->next;
    5.90      }
    5.91 -    return device;
    5.92 +    return NULL;
    5.93  }
    5.94  
    5.95  static SDL_HIDAPI_Device *
    5.96 @@ -469,6 +454,52 @@
    5.97      return device;
    5.98  }
    5.99  
   5.100 +static void
   5.101 +HIDAPI_SetupDeviceDriver(SDL_HIDAPI_Device *device)
   5.102 +{
   5.103 +    if (device->driver) {
   5.104 +        /* Already setup */
   5.105 +        return;
   5.106 +    }
   5.107 +
   5.108 +    device->driver = HIDAPI_GetDeviceDriver(device);
   5.109 +    if (device->driver) {
   5.110 +        const char *name = device->driver->GetDeviceName(device->vendor_id, device->product_id);
   5.111 +        if (name) {
   5.112 +            SDL_free(device->name);
   5.113 +            device->name = SDL_strdup(name);
   5.114 +        }
   5.115 +    }
   5.116 +
   5.117 +    /* Initialize the device, which may cause a connected event */
   5.118 +    if (device->driver && !device->driver->InitDevice(device)) {
   5.119 +        device->driver = NULL;
   5.120 +    }
   5.121 +}
   5.122 +
   5.123 +static void
   5.124 +HIDAPI_CleanupDeviceDriver(SDL_HIDAPI_Device *device)
   5.125 +{
   5.126 +    int i;
   5.127 +
   5.128 +    if (!device->driver) {
   5.129 +        /* Already cleaned up */
   5.130 +        return;
   5.131 +    }
   5.132 +
   5.133 +    /* Disconnect any joysticks */
   5.134 +    for (i = 0; i < device->num_joysticks; ++i) {
   5.135 +        SDL_Joystick *joystick = SDL_JoystickFromInstanceID(device->joysticks[i]);
   5.136 +        if (joystick) {
   5.137 +            HIDAPI_JoystickClose(joystick);
   5.138 +        }
   5.139 +        HIDAPI_JoystickDisconnected(device, device->joysticks[i]);
   5.140 +    }
   5.141 +
   5.142 +    device->driver->FreeDevice(device);
   5.143 +    device->driver = NULL;
   5.144 +}
   5.145 +
   5.146  static void SDLCALL
   5.147  SDL_HIDAPIDriverHintChanged(void *userdata, const char *name, const char *oldValue, const char *hint)
   5.148  {
   5.149 @@ -500,31 +531,19 @@
   5.150      }
   5.151  
   5.152      /* Update device list if driver availability changes */
   5.153 -    while (device) {
   5.154 -        if (device->driver) {
   5.155 -            if (!device->driver->enabled) {
   5.156 -                device->driver = NULL;
   5.157 -
   5.158 -                --SDL_HIDAPI_numjoysticks;
   5.159 +    SDL_LockMutex(SDL_HIDAPI_mutex);
   5.160  
   5.161 -                SDL_PrivateJoystickRemoved(device->instance_id);
   5.162 -            }
   5.163 -        } else {
   5.164 -            device->driver = HIDAPI_GetDeviceDriver(device);
   5.165 -            if (device->driver) {
   5.166 -                device->instance_id = SDL_GetNextJoystickInstanceID();
   5.167 -
   5.168 -                ++SDL_HIDAPI_numjoysticks;
   5.169 -
   5.170 -                SDL_PrivateJoystickAdded(device->instance_id);
   5.171 -            }
   5.172 +    while (device) {
   5.173 +        if (device->driver && !device->driver->enabled) {
   5.174 +            HIDAPI_CleanupDeviceDriver(device);
   5.175          }
   5.176 +        HIDAPI_SetupDeviceDriver(device);
   5.177          device = device->next;
   5.178      }
   5.179 +
   5.180 +    SDL_UnlockMutex(SDL_HIDAPI_mutex);
   5.181  }
   5.182  
   5.183 -static void HIDAPI_JoystickDetect(void);
   5.184 -
   5.185  static int
   5.186  HIDAPI_JoystickInit(void)
   5.187  {
   5.188 @@ -539,6 +558,8 @@
   5.189          return -1;
   5.190      }
   5.191  
   5.192 +    SDL_HIDAPI_mutex = SDL_CreateMutex();
   5.193 +
   5.194      for (i = 0; i < SDL_arraysize(SDL_HIDAPI_drivers); ++i) {
   5.195          SDL_HIDAPI_DeviceDriver *driver = SDL_HIDAPI_drivers[i];
   5.196          SDL_AddHintCallback(driver->hint, SDL_HIDAPIDriverHintChanged, NULL);
   5.197 @@ -553,6 +574,47 @@
   5.198      return 0;
   5.199  }
   5.200  
   5.201 +SDL_bool
   5.202 +HIDAPI_JoystickConnected(SDL_HIDAPI_Device *device, SDL_JoystickID *pJoystickID)
   5.203 +{
   5.204 +    SDL_JoystickID joystickID;
   5.205 +    SDL_JoystickID *joysticks = (SDL_JoystickID *)SDL_realloc(device->joysticks, (device->num_joysticks + 1)*sizeof(*device->joysticks));
   5.206 +    if (!joysticks) {
   5.207 +        return SDL_FALSE;
   5.208 +    }
   5.209 +
   5.210 +    joystickID = SDL_GetNextJoystickInstanceID();
   5.211 +    device->joysticks = joysticks;
   5.212 +    device->joysticks[device->num_joysticks++] = joystickID;
   5.213 +    ++SDL_HIDAPI_numjoysticks;
   5.214 +
   5.215 +    SDL_PrivateJoystickAdded(joystickID);
   5.216 +
   5.217 +    if (pJoystickID) {
   5.218 +        *pJoystickID = joystickID;
   5.219 +    }
   5.220 +    return SDL_TRUE;
   5.221 +}
   5.222 +
   5.223 +void
   5.224 +HIDAPI_JoystickDisconnected(SDL_HIDAPI_Device *device, SDL_JoystickID joystickID)
   5.225 +{
   5.226 +    int i;
   5.227 +
   5.228 +    for (i = 0; i < device->num_joysticks; ++i) {
   5.229 +        if (device->joysticks[i] == joystickID) {
   5.230 +            SDL_memcpy(&device->joysticks[i], &device->joysticks[i+1], device->num_joysticks - i - 1);
   5.231 +            --device->num_joysticks;
   5.232 +            --SDL_HIDAPI_numjoysticks;
   5.233 +
   5.234 +            if (!shutting_down) {
   5.235 +                SDL_PrivateJoystickRemoved(joystickID);
   5.236 +            }
   5.237 +            return;
   5.238 +        }
   5.239 +    }
   5.240 +}
   5.241 +
   5.242  static int
   5.243  HIDAPI_JoystickGetCount(void)
   5.244  {
   5.245 @@ -573,7 +635,11 @@
   5.246      if (!device) {
   5.247          return;
   5.248      }
   5.249 -    device->instance_id = -1;
   5.250 +    device->path = SDL_strdup(info->path);
   5.251 +    if (!device->path) {
   5.252 +        SDL_free(device);
   5.253 +        return;
   5.254 +    }
   5.255      device->seen = SDL_TRUE;
   5.256      device->vendor_id = info->vendor_id;
   5.257      device->product_id = info->product_id;
   5.258 @@ -651,33 +717,13 @@
   5.259          size_t name_size = (6 + 1 + 6 + 1);
   5.260          device->name = (char *)SDL_malloc(name_size);
   5.261          if (!device->name) {
   5.262 +            SDL_free(device->path);
   5.263              SDL_free(device);
   5.264              return;
   5.265          }
   5.266          SDL_snprintf(device->name, name_size, "0x%.4x/0x%.4x", info->vendor_id, info->product_id);
   5.267      }
   5.268  
   5.269 -    device->driver = HIDAPI_GetDeviceDriver(device);
   5.270 -
   5.271 -    if (device->driver) {
   5.272 -        const char *name = device->driver->GetDeviceName(device->vendor_id, device->product_id);
   5.273 -        if (name) {
   5.274 -            SDL_free(device->name);
   5.275 -            device->name = SDL_strdup(name);
   5.276 -        }
   5.277 -    }
   5.278 -
   5.279 -    device->path = SDL_strdup(info->path);
   5.280 -    if (!device->path) {
   5.281 -        SDL_free(device->name);
   5.282 -        SDL_free(device);
   5.283 -        return;
   5.284 -    }
   5.285 -
   5.286 -#ifdef DEBUG_HIDAPI
   5.287 -    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");
   5.288 -#endif
   5.289 -
   5.290      /* Add it to the list */
   5.291      if (last) {
   5.292          last->next = device;
   5.293 @@ -685,19 +731,16 @@
   5.294          SDL_HIDAPI_devices = device;
   5.295      }
   5.296  
   5.297 -    if (device->driver) {
   5.298 -        /* It's a joystick! */
   5.299 -        device->instance_id = SDL_GetNextJoystickInstanceID();
   5.300 +#ifdef DEBUG_HIDAPI
   5.301 +    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");
   5.302 +#endif
   5.303  
   5.304 -        ++SDL_HIDAPI_numjoysticks;
   5.305 -
   5.306 -        SDL_PrivateJoystickAdded(device->instance_id);
   5.307 -    }
   5.308 +    HIDAPI_SetupDeviceDriver(device);
   5.309  }
   5.310  
   5.311  
   5.312  static void
   5.313 -HIDAPI_DelDevice(SDL_HIDAPI_Device *device, SDL_bool send_event)
   5.314 +HIDAPI_DelDevice(SDL_HIDAPI_Device *device)
   5.315  {
   5.316      SDL_HIDAPI_Device *curr, *last;
   5.317      for (curr = SDL_HIDAPI_devices, last = NULL; curr; last = curr, curr = curr->next) {
   5.318 @@ -708,12 +751,7 @@
   5.319                  SDL_HIDAPI_devices = curr->next;
   5.320              }
   5.321  
   5.322 -            if (device->driver && send_event) {
   5.323 -                /* Need to decrement the joystick count before we post the event */
   5.324 -                --SDL_HIDAPI_numjoysticks;
   5.325 -
   5.326 -                SDL_PrivateJoystickRemoved(device->instance_id);
   5.327 -            }
   5.328 +            HIDAPI_CleanupDeviceDriver(device);
   5.329  
   5.330              SDL_free(device->name);
   5.331              SDL_free(device->path);
   5.332 @@ -729,6 +767,8 @@
   5.333      SDL_HIDAPI_Device *device;
   5.334      struct hid_device_info *devs, *info;
   5.335  
   5.336 +    SDL_LockMutex(SDL_HIDAPI_mutex);
   5.337 +
   5.338      /* Prepare the existing device list */
   5.339      device = SDL_HIDAPI_devices;
   5.340      while (device) {
   5.341 @@ -758,10 +798,12 @@
   5.342          SDL_HIDAPI_Device *next = device->next;
   5.343  
   5.344          if (!device->seen) {
   5.345 -            HIDAPI_DelDevice(device, SDL_TRUE);
   5.346 +            HIDAPI_DelDevice(device);
   5.347          }
   5.348          device = next;
   5.349      }
   5.350 +
   5.351 +    SDL_UnlockMutex(SDL_HIDAPI_mutex);
   5.352  }
   5.353  
   5.354  SDL_bool
   5.355 @@ -799,18 +841,45 @@
   5.356  static void
   5.357  HIDAPI_JoystickDetect(void)
   5.358  {
   5.359 +    SDL_HIDAPI_Device *device;
   5.360 +
   5.361      HIDAPI_UpdateDiscovery();
   5.362      if (SDL_HIDAPI_discovery.m_bHaveDevicesChanged) {
   5.363          /* FIXME: We probably need to schedule an update in a few seconds as well */
   5.364          HIDAPI_UpdateDeviceList();
   5.365          SDL_HIDAPI_discovery.m_bHaveDevicesChanged = SDL_FALSE;
   5.366      }
   5.367 +
   5.368 +    /* Update the devices, which may change connected joysticks and send events */
   5.369 +    SDL_LockMutex(SDL_HIDAPI_mutex);
   5.370 +
   5.371 +    /* Prepare the existing device list */
   5.372 +    device = SDL_HIDAPI_devices;
   5.373 +    while (device) {
   5.374 +        if (device->driver) {
   5.375 +            device->driver->UpdateDevice(device);
   5.376 +        }
   5.377 +        device = device->next;
   5.378 +    }
   5.379 +
   5.380 +    SDL_UnlockMutex(SDL_HIDAPI_mutex);
   5.381  }
   5.382  
   5.383  static const char *
   5.384  HIDAPI_JoystickGetDeviceName(int device_index)
   5.385  {
   5.386 -    return HIDAPI_GetJoystickByIndex(device_index)->name;
   5.387 +    SDL_HIDAPI_Device *device;
   5.388 +    const char *name = NULL;
   5.389 +
   5.390 +    SDL_LockMutex(SDL_HIDAPI_mutex);
   5.391 +    device = HIDAPI_GetDeviceByIndex(device_index, NULL);
   5.392 +    if (device) {
   5.393 +        /* FIXME: The device could be freed after this name is returned... */
   5.394 +        name = device->name;
   5.395 +    }
   5.396 +    SDL_UnlockMutex(SDL_HIDAPI_mutex);
   5.397 +
   5.398 +    return name;
   5.399  }
   5.400  
   5.401  static int
   5.402 @@ -822,36 +891,45 @@
   5.403  static SDL_JoystickGUID
   5.404  HIDAPI_JoystickGetDeviceGUID(int device_index)
   5.405  {
   5.406 -    return HIDAPI_GetJoystickByIndex(device_index)->guid;
   5.407 +    SDL_HIDAPI_Device *device;
   5.408 +    SDL_JoystickGUID guid;
   5.409 +
   5.410 +    SDL_LockMutex(SDL_HIDAPI_mutex);
   5.411 +    device = HIDAPI_GetDeviceByIndex(device_index, NULL);
   5.412 +    if (device) {
   5.413 +        SDL_memcpy(&guid, &device->guid, sizeof(guid));
   5.414 +    } else {
   5.415 +        SDL_zero(guid);
   5.416 +    }
   5.417 +    SDL_UnlockMutex(SDL_HIDAPI_mutex);
   5.418 +
   5.419 +    return guid;
   5.420  }
   5.421  
   5.422  static SDL_JoystickID
   5.423  HIDAPI_JoystickGetDeviceInstanceID(int device_index)
   5.424  {
   5.425 -    return HIDAPI_GetJoystickByIndex(device_index)->instance_id;
   5.426 +    SDL_JoystickID joystickID;
   5.427 +    SDL_LockMutex(SDL_HIDAPI_mutex);
   5.428 +    HIDAPI_GetDeviceByIndex(device_index, &joystickID);
   5.429 +    SDL_UnlockMutex(SDL_HIDAPI_mutex);
   5.430 +    return joystickID;
   5.431  }
   5.432  
   5.433  static int
   5.434  HIDAPI_JoystickOpen(SDL_Joystick * joystick, int device_index)
   5.435  {
   5.436 -    SDL_HIDAPI_Device *device = HIDAPI_GetJoystickByIndex(device_index);
   5.437 +    SDL_JoystickID joystickID;
   5.438 +    SDL_HIDAPI_Device *device = HIDAPI_GetDeviceByIndex(device_index, &joystickID);
   5.439      struct joystick_hwdata *hwdata;
   5.440  
   5.441      hwdata = (struct joystick_hwdata *)SDL_calloc(1, sizeof(*hwdata));
   5.442      if (!hwdata) {
   5.443          return SDL_OutOfMemory();
   5.444      }
   5.445 +    hwdata->device = device;
   5.446  
   5.447 -    hwdata->driver = device->driver;
   5.448 -    hwdata->dev = hid_open_path(device->path, 0);
   5.449 -    if (!hwdata->dev) {
   5.450 -        SDL_free(hwdata);
   5.451 -        return SDL_SetError("Couldn't open HID device %s", device->path);
   5.452 -    }
   5.453 -    hwdata->mutex = SDL_CreateMutex();
   5.454 -
   5.455 -    if (!device->driver->Init(joystick, hwdata->dev, device->vendor_id, device->product_id, &hwdata->context)) {
   5.456 -        hid_close(hwdata->dev);
   5.457 +    if (!device->driver->OpenJoystick(device, joystick)) {
   5.458          SDL_free(hwdata);
   5.459          return -1;
   5.460      }
   5.461 @@ -863,49 +941,41 @@
   5.462  static int
   5.463  HIDAPI_JoystickRumble(SDL_Joystick * joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms)
   5.464  {
   5.465 -    struct joystick_hwdata *hwdata = joystick->hwdata;
   5.466 -    SDL_HIDAPI_DeviceDriver *driver = hwdata->driver;
   5.467      int result;
   5.468  
   5.469 -    SDL_LockMutex(hwdata->mutex);
   5.470 -    result = driver->Rumble(joystick, hwdata->dev, hwdata->context, low_frequency_rumble, high_frequency_rumble, duration_ms);
   5.471 -    SDL_UnlockMutex(hwdata->mutex);
   5.472 +    SDL_LockMutex(SDL_HIDAPI_mutex);
   5.473 +    if (joystick->hwdata) {
   5.474 +        SDL_HIDAPI_Device *device = joystick->hwdata->device;
   5.475 +
   5.476 +        result = device->driver->RumbleJoystick(device, joystick, low_frequency_rumble, high_frequency_rumble, duration_ms);
   5.477 +    } else {
   5.478 +        SDL_SetError("Rumble failed, device disconnected");
   5.479 +        result = -1;
   5.480 +    }
   5.481 +    SDL_UnlockMutex(SDL_HIDAPI_mutex);
   5.482 +
   5.483      return result;
   5.484  }
   5.485  
   5.486  static void
   5.487  HIDAPI_JoystickUpdate(SDL_Joystick * joystick)
   5.488  {
   5.489 -    struct joystick_hwdata *hwdata = joystick->hwdata;
   5.490 -    SDL_HIDAPI_DeviceDriver *driver = hwdata->driver;
   5.491 -    SDL_bool succeeded;
   5.492 -
   5.493 -    SDL_LockMutex(hwdata->mutex);
   5.494 -    succeeded = driver->Update(joystick, hwdata->dev, hwdata->context);
   5.495 -    SDL_UnlockMutex(hwdata->mutex);
   5.496 -    
   5.497 -    if (!succeeded) {
   5.498 -        SDL_HIDAPI_Device *device;
   5.499 -        for (device = SDL_HIDAPI_devices; device; device = device->next) {
   5.500 -            if (device->instance_id == joystick->instance_id) {
   5.501 -                HIDAPI_DelDevice(device, SDL_TRUE);
   5.502 -                break;
   5.503 -            }
   5.504 -        }
   5.505 -    }
   5.506 +    /* This is handled in HIDAPI_JoystickDetect() */
   5.507  }
   5.508  
   5.509  static void
   5.510  HIDAPI_JoystickClose(SDL_Joystick * joystick)
   5.511  {
   5.512 -    struct joystick_hwdata *hwdata = joystick->hwdata;
   5.513 -    SDL_HIDAPI_DeviceDriver *driver = hwdata->driver;
   5.514 -    driver->Quit(joystick, hwdata->dev, hwdata->context);
   5.515 +    SDL_LockMutex(SDL_HIDAPI_mutex);
   5.516 +    if (joystick->hwdata) {
   5.517 +        SDL_HIDAPI_Device *device = joystick->hwdata->device;
   5.518  
   5.519 -    hid_close(hwdata->dev);
   5.520 -    SDL_DestroyMutex(hwdata->mutex);
   5.521 -    SDL_free(hwdata);
   5.522 -    joystick->hwdata = NULL;
   5.523 +        device->driver->CloseJoystick(device, joystick);
   5.524 +
   5.525 +        SDL_free(joystick->hwdata);
   5.526 +        joystick->hwdata = NULL;
   5.527 +    }
   5.528 +    SDL_UnlockMutex(SDL_HIDAPI_mutex);
   5.529  }
   5.530  
   5.531  static void
   5.532 @@ -913,10 +983,12 @@
   5.533  {
   5.534      int i;
   5.535  
   5.536 +    shutting_down = SDL_TRUE;
   5.537 +
   5.538      HIDAPI_ShutdownDiscovery();
   5.539  
   5.540      while (SDL_HIDAPI_devices) {
   5.541 -        HIDAPI_DelDevice(SDL_HIDAPI_devices, SDL_FALSE);
   5.542 +        HIDAPI_DelDevice(SDL_HIDAPI_devices);
   5.543      }
   5.544      for (i = 0; i < SDL_arraysize(SDL_HIDAPI_drivers); ++i) {
   5.545          SDL_HIDAPI_DeviceDriver *driver = SDL_HIDAPI_drivers[i];
   5.546 @@ -924,10 +996,14 @@
   5.547      }
   5.548      SDL_DelHintCallback(SDL_HINT_JOYSTICK_HIDAPI,
   5.549                          SDL_HIDAPIDriverHintChanged, NULL);
   5.550 -    SDL_HIDAPI_numjoysticks = 0;
   5.551 +    SDL_DestroyMutex(SDL_HIDAPI_mutex);
   5.552  
   5.553      hid_exit();
   5.554  
   5.555 +    /* Make sure the drivers cleaned up properly */
   5.556 +    SDL_assert(SDL_HIDAPI_numjoysticks == 0);
   5.557 +
   5.558 +    shutting_down = SDL_FALSE;
   5.559      initialized = SDL_FALSE;
   5.560  }
   5.561  
     6.1 --- a/src/joystick/hidapi/SDL_hidapijoystick_c.h	Thu Dec 19 13:54:03 2019 +0100
     6.2 +++ b/src/joystick/hidapi/SDL_hidapijoystick_c.h	Thu Dec 19 15:01:30 2019 -0800
     6.3 @@ -46,16 +46,45 @@
     6.4  /* Prevent rumble duration overflow */
     6.5  #define SDL_MAX_RUMBLE_DURATION_MS  0x0fffffff
     6.6  
     6.7 +/* Forward declaration */
     6.8 +struct _SDL_HIDAPI_DeviceDriver;
     6.9 +
    6.10 +typedef struct _SDL_HIDAPI_Device
    6.11 +{
    6.12 +    char *name;
    6.13 +    char *path;
    6.14 +    Uint16 vendor_id;
    6.15 +    Uint16 product_id;
    6.16 +    Uint16 version;
    6.17 +    SDL_JoystickGUID guid;
    6.18 +    int interface_number;   /* Available on Windows and Linux */
    6.19 +    Uint16 usage_page;      /* Available on Windows and Mac OS X */
    6.20 +    Uint16 usage;           /* Available on Windows and Mac OS X */
    6.21 +
    6.22 +    struct _SDL_HIDAPI_DeviceDriver *driver;
    6.23 +    void *context;
    6.24 +    hid_device *dev;
    6.25 +    int num_joysticks;
    6.26 +    SDL_JoystickID *joysticks;
    6.27 +
    6.28 +    /* Used during scanning for device changes */
    6.29 +    SDL_bool seen;
    6.30 +
    6.31 +    struct _SDL_HIDAPI_Device *next;
    6.32 +} SDL_HIDAPI_Device;
    6.33 +
    6.34  typedef struct _SDL_HIDAPI_DeviceDriver
    6.35  {
    6.36      const char *hint;
    6.37      SDL_bool enabled;
    6.38      SDL_bool (*IsSupportedDevice)(Uint16 vendor_id, Uint16 product_id, Uint16 version, int interface_number, const char *name);
    6.39      const char *(*GetDeviceName)(Uint16 vendor_id, Uint16 product_id);
    6.40 -    SDL_bool (*Init)(SDL_Joystick *joystick, hid_device *dev, Uint16 vendor_id, Uint16 product_id, void **context);
    6.41 -    int (*Rumble)(SDL_Joystick *joystick, hid_device *dev, void *context, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms);
    6.42 -    SDL_bool (*Update)(SDL_Joystick *joystick, hid_device *dev, void *context);
    6.43 -    void (*Quit)(SDL_Joystick *joystick, hid_device *dev, void *context);
    6.44 +    SDL_bool (*InitDevice)(SDL_HIDAPI_Device *device);
    6.45 +    SDL_bool (*UpdateDevice)(SDL_HIDAPI_Device *device);
    6.46 +    SDL_bool (*OpenJoystick)(SDL_HIDAPI_Device *device, SDL_Joystick *joystick);
    6.47 +    int (*RumbleJoystick)(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 low_frequency_rumble, Uint16 high_frequency_rumble, Uint32 duration_ms);
    6.48 +    void (*CloseJoystick)(SDL_HIDAPI_Device *device, SDL_Joystick *joystick);
    6.49 +    void (*FreeDevice)(SDL_HIDAPI_Device *device);
    6.50  
    6.51  } SDL_HIDAPI_DeviceDriver;
    6.52  
    6.53 @@ -69,6 +98,9 @@
    6.54  /* Return true if a HID device is present and supported as a joystick */
    6.55  extern SDL_bool HIDAPI_IsDevicePresent(Uint16 vendor_id, Uint16 product_id, Uint16 version, const char *name);
    6.56  
    6.57 +extern SDL_bool HIDAPI_JoystickConnected(SDL_HIDAPI_Device *device, SDL_JoystickID *pJoystickID);
    6.58 +extern void HIDAPI_JoystickDisconnected(SDL_HIDAPI_Device *device, SDL_JoystickID joystickID);
    6.59 +
    6.60  #endif /* SDL_JOYSTICK_HIDAPI_H */
    6.61  
    6.62  /* vi: set ts=4 sw=4 expandtab: */