Separate joystick power state into battery status and percentage

This allows you to see battery percentage while the controller is charging
This commit is contained in:
Sam Lantinga
2024-03-30 10:55:13 -07:00
parent 5e624c2083
commit 8847b35244
42 changed files with 596 additions and 1001 deletions

View File

@@ -392,7 +392,11 @@ static SDL_bool HIDAPI_DriverGameCube_OpenJoystick(SDL_HIDAPI_Device *device, SD
if (joystick->instance_id == ctx->joysticks[i]) {
joystick->nbuttons = 12;
joystick->naxes = SDL_GAMEPAD_AXIS_MAX;
joystick->epowerlevel = ctx->wireless[i] ? SDL_JOYSTICK_POWER_UNKNOWN : SDL_JOYSTICK_POWER_WIRED;
if (ctx->wireless[i]) {
joystick->connection_state = SDL_JOYSTICK_CONNECTION_WIRELESS;
} else {
joystick->connection_state = SDL_JOYSTICK_CONNECTION_WIRED;
}
return SDL_TRUE;
}
}

View File

@@ -103,7 +103,6 @@ static SDL_bool HIDAPI_DriverLuna_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Jo
joystick->nbuttons = SDL_GAMEPAD_NUM_LUNA_BUTTONS;
joystick->naxes = SDL_GAMEPAD_AXIS_MAX;
joystick->nhats = 1;
joystick->epowerlevel = SDL_JOYSTICK_POWER_FULL;
return SDL_TRUE;
}
@@ -259,17 +258,8 @@ static void HIDAPI_DriverLuna_HandleBluetoothStatePacket(SDL_Joystick *joystick,
if (size >= 2 && data[0] == 0x04) {
/* Battery level report */
int level = data[1] * 100 / 0xFF;
if (level == 0) {
SDL_SendJoystickBatteryLevel(joystick, SDL_JOYSTICK_POWER_EMPTY);
} else if (level <= 20) {
SDL_SendJoystickBatteryLevel(joystick, SDL_JOYSTICK_POWER_LOW);
} else if (level <= 70) {
SDL_SendJoystickBatteryLevel(joystick, SDL_JOYSTICK_POWER_MEDIUM);
} else {
SDL_SendJoystickBatteryLevel(joystick, SDL_JOYSTICK_POWER_FULL);
}
int percent = (int)SDL_roundf((data[1] / 255.0f) * 100.0f);
SDL_SendJoystickPowerInfo(joystick, SDL_POWERSTATE_ON_BATTERY, percent);
return;
}

View File

@@ -279,7 +279,6 @@ static SDL_bool HIDAPI_DriverPS3_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joy
joystick->nbuttons = 11;
joystick->naxes = 16;
joystick->nhats = 1;
joystick->epowerlevel = SDL_JOYSTICK_POWER_WIRED;
SDL_PrivateJoystickAddSensor(joystick, SDL_SENSOR_ACCEL, 100.0f);
@@ -682,7 +681,6 @@ static SDL_bool HIDAPI_DriverPS3ThirdParty_OpenJoystick(SDL_HIDAPI_Device *devic
joystick->nbuttons = 11;
joystick->naxes = 16;
joystick->nhats = 1;
joystick->epowerlevel = SDL_JOYSTICK_POWER_WIRED;
return SDL_TRUE;
}
@@ -1127,7 +1125,6 @@ static SDL_bool HIDAPI_DriverPS3SonySixaxis_OpenJoystick(SDL_HIDAPI_Device *devi
joystick->nbuttons = 11;
joystick->naxes = 16;
joystick->nhats = 1;
joystick->epowerlevel = SDL_JOYSTICK_POWER_WIRED;
SDL_PrivateJoystickAddSensor(joystick, SDL_SENSOR_ACCEL, 100.0f);

View File

@@ -720,7 +720,7 @@ static void HIDAPI_DriverPS4_SetEnhancedModeAvailable(SDL_DriverPS4_Context *ctx
SDL_PrivateJoystickAddSensor(ctx->joystick, SDL_SENSOR_ACCEL, 250.0f);
}
if (ctx->device->is_bluetooth && ctx->official_controller) {
if (ctx->official_controller) {
ctx->report_battery = SDL_TRUE;
}
@@ -833,12 +833,6 @@ static SDL_bool HIDAPI_DriverPS4_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joy
}
joystick->naxes = SDL_GAMEPAD_AXIS_MAX;
joystick->nhats = 1;
if (device->is_bluetooth) {
/* We'll update this once we're in enhanced mode */
joystick->epowerlevel = SDL_JOYSTICK_POWER_UNKNOWN;
} else {
joystick->epowerlevel = SDL_JOYSTICK_POWER_WIRED;
}
SDL_AddHintCallback(SDL_HINT_JOYSTICK_HIDAPI_PS4_RUMBLE,
SDL_PS4RumbleHintChanged, ctx);
@@ -1073,17 +1067,26 @@ static void HIDAPI_DriverPS4_HandleStatePacket(SDL_Joystick *joystick, SDL_hid_d
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTY, axis);
if (size > 9 && ctx->report_battery && ctx->enhanced_reports) {
/* Battery level ranges from 0 to 10 */
int level = (packet->ucBatteryLevel & 0xF);
if (level == 0) {
SDL_SendJoystickBatteryLevel(joystick, SDL_JOYSTICK_POWER_EMPTY);
} else if (level <= 2) {
SDL_SendJoystickBatteryLevel(joystick, SDL_JOYSTICK_POWER_LOW);
} else if (level <= 7) {
SDL_SendJoystickBatteryLevel(joystick, SDL_JOYSTICK_POWER_MEDIUM);
SDL_PowerState state;
int percent;
Uint8 level = (packet->ucBatteryLevel & 0x0F);
if (packet->ucBatteryLevel & 0x10) {
if (level <= 10) {
state = SDL_POWERSTATE_CHARGING;
percent = SDL_min(level * 10 + 5, 100);
} else if (level == 11) {
state = SDL_POWERSTATE_CHARGED;
percent = 100;
} else {
state = SDL_POWERSTATE_UNKNOWN;
percent = 0;
}
} else {
SDL_SendJoystickBatteryLevel(joystick, SDL_JOYSTICK_POWER_FULL);
state = SDL_POWERSTATE_ON_BATTERY;
percent = SDL_min(level * 10 + 5, 100);
}
SDL_SendJoystickPowerInfo(joystick, state, percent);
}
if (size > 9 && ctx->report_touchpad && ctx->enhanced_reports) {

View File

@@ -815,9 +815,7 @@ static void HIDAPI_DriverPS5_SetEnhancedModeAvailable(SDL_DriverPS5_Context *ctx
}
}
if (ctx->device->is_bluetooth) {
ctx->report_battery = SDL_TRUE;
}
ctx->report_battery = SDL_TRUE;
HIDAPI_UpdateDeviceProperties(ctx->device);
}
@@ -956,12 +954,6 @@ static SDL_bool HIDAPI_DriverPS5_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joy
}
joystick->naxes = SDL_GAMEPAD_AXIS_MAX;
joystick->nhats = 1;
if (device->is_bluetooth) {
/* We'll update this once we're in enhanced mode */
joystick->epowerlevel = SDL_JOYSTICK_POWER_UNKNOWN;
} else {
joystick->epowerlevel = SDL_JOYSTICK_POWER_WIRED;
}
joystick->firmware_version = ctx->firmware_version;
SDL_AddHintCallback(SDL_HINT_JOYSTICK_HIDAPI_PS5_RUMBLE,
@@ -1387,17 +1379,30 @@ static void HIDAPI_DriverPS5_HandleStatePacket(SDL_Joystick *joystick, SDL_hid_d
}
if (ctx->report_battery) {
/* Battery level ranges from 0 to 10 */
int level = (packet->ucBatteryLevel & 0xF);
if (level == 0) {
SDL_SendJoystickBatteryLevel(joystick, SDL_JOYSTICK_POWER_EMPTY);
} else if (level <= 2) {
SDL_SendJoystickBatteryLevel(joystick, SDL_JOYSTICK_POWER_LOW);
} else if (level <= 7) {
SDL_SendJoystickBatteryLevel(joystick, SDL_JOYSTICK_POWER_MEDIUM);
} else {
SDL_SendJoystickBatteryLevel(joystick, SDL_JOYSTICK_POWER_FULL);
SDL_PowerState state;
int percent;
Uint8 status = (packet->ucBatteryLevel >> 4) & 0x0F;
Uint8 level = (packet->ucBatteryLevel & 0x0F);
switch (status) {
case 0:
state = SDL_POWERSTATE_ON_BATTERY;
percent = SDL_min(level * 10 + 5, 100);
break;
case 1:
state = SDL_POWERSTATE_CHARGING;
percent = SDL_min(level * 10 + 5, 100);
break;
case 2:
state = SDL_POWERSTATE_CHARGED;
percent = 100;
break;
default:
state = SDL_POWERSTATE_UNKNOWN;
percent = 0;
break;
}
SDL_SendJoystickPowerInfo(joystick, state, percent);
}
SDL_memcpy(&ctx->last_state, packet, sizeof(ctx->last_state));

View File

@@ -77,8 +77,10 @@ typedef struct
{
Uint8 seq_num;
SDL_JoystickPowerLevel battery_level;
SDL_bool charging;
SDL_bool has_charging;
Uint8 charging;
SDL_bool has_battery_level;
Uint8 battery_level;
Uint64 last_battery_query_time;
SDL_bool rumble_report_pending;
@@ -184,14 +186,12 @@ static SDL_bool HIDAPI_DriverShield_OpenJoystick(SDL_HIDAPI_Device *device, SDL_
joystick->nbuttons = SDL_GAMEPAD_NUM_SHIELD_V103_BUTTONS;
joystick->naxes = SDL_GAMEPAD_AXIS_MAX;
joystick->nhats = 1;
joystick->epowerlevel = SDL_JOYSTICK_POWER_WIRED;
SDL_PrivateJoystickAddTouchpad(joystick, 1);
} else {
joystick->nbuttons = SDL_GAMEPAD_NUM_SHIELD_V104_BUTTONS;
joystick->naxes = SDL_GAMEPAD_AXIS_MAX;
joystick->nhats = 1;
joystick->epowerlevel = SDL_JOYSTICK_POWER_UNKNOWN;
}
/* Request battery and charging info */
@@ -451,6 +451,17 @@ static void HIDAPI_DriverShield_HandleStatePacketV104(SDL_Joystick *joystick, SD
SDL_memcpy(ctx->last_state, data, SDL_min(size, sizeof(ctx->last_state)));
}
static void HIDAPI_DriverShield_UpdatePowerInfo(SDL_Joystick *joystick, SDL_DriverShield_Context *ctx)
{
if (!ctx->has_charging || !ctx->has_battery_level) {
return;
}
SDL_PowerState state = ctx->charging ? SDL_POWERSTATE_CHARGING : SDL_POWERSTATE_ON_BATTERY;
int percent = ctx->battery_level * 20;
SDL_SendJoystickPowerInfo(joystick, state, percent);
}
static SDL_bool HIDAPI_DriverShield_UpdateDevice(SDL_HIDAPI_Device *device)
{
SDL_DriverShield_Context *ctx = (SDL_DriverShield_Context *)device->context;
@@ -496,34 +507,14 @@ static SDL_bool HIDAPI_DriverShield_UpdateDevice(SDL_HIDAPI_Device *device)
HIDAPI_DriverShield_SendNextRumble(device);
break;
case CMD_CHARGE_STATE:
ctx->charging = cmd_resp_report->payload[0] != 0;
if (joystick) {
SDL_SendJoystickBatteryLevel(joystick, ctx->charging ? SDL_JOYSTICK_POWER_WIRED : ctx->battery_level);
}
ctx->has_charging = SDL_TRUE;
ctx->charging = cmd_resp_report->payload[0];
HIDAPI_DriverShield_UpdatePowerInfo(joystick, ctx);
break;
case CMD_BATTERY_STATE:
switch (cmd_resp_report->payload[2]) {
case 0:
ctx->battery_level = SDL_JOYSTICK_POWER_EMPTY;
break;
case 1:
ctx->battery_level = SDL_JOYSTICK_POWER_LOW;
break;
case 2: /* 40% */
case 3: /* 60% */
case 4: /* 80% */
ctx->battery_level = SDL_JOYSTICK_POWER_MEDIUM;
break;
case 5:
ctx->battery_level = SDL_JOYSTICK_POWER_FULL;
break;
default:
ctx->battery_level = SDL_JOYSTICK_POWER_UNKNOWN;
break;
}
if (joystick) {
SDL_SendJoystickBatteryLevel(joystick, ctx->charging ? SDL_JOYSTICK_POWER_WIRED : ctx->battery_level);
}
ctx->has_battery_level = SDL_TRUE;
ctx->battery_level = cmd_resp_report->payload[2];
HIDAPI_DriverShield_UpdatePowerInfo(joystick, ctx);
break;
}
break;

View File

@@ -109,7 +109,6 @@ static SDL_bool HIDAPI_DriverStadia_OpenJoystick(SDL_HIDAPI_Device *device, SDL_
joystick->nbuttons = SDL_GAMEPAD_NUM_STADIA_BUTTONS;
joystick->naxes = SDL_GAMEPAD_AXIS_MAX;
joystick->nhats = 1;
joystick->epowerlevel = SDL_JOYSTICK_POWER_WIRED;
return SDL_TRUE;
}

View File

@@ -1447,7 +1447,6 @@ static SDL_bool HIDAPI_DriverSwitch_OpenJoystick(SDL_HIDAPI_Device *device, SDL_
joystick->nbuttons = SDL_GAMEPAD_NUM_SWITCH_BUTTONS;
joystick->naxes = SDL_GAMEPAD_AXIS_MAX;
joystick->nhats = 1;
joystick->epowerlevel = device->is_bluetooth ? SDL_JOYSTICK_POWER_UNKNOWN : SDL_JOYSTICK_POWER_WIRED;
/* Set up for input */
ctx->m_bSyncWrite = SDL_FALSE;
@@ -2095,29 +2094,33 @@ static void HandleFullControllerState(SDL_Joystick *joystick, SDL_DriverSwitch_C
SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTY, ~axis);
}
if (ctx->device->is_bluetooth) {
/* High nibble of battery/connection byte is battery level, low nibble is connection status
* LSB of connection nibble is USB/Switch connection status
*/
if (packet->controllerState.ucBatteryAndConnection & 0x1) {
SDL_SendJoystickBatteryLevel(joystick, SDL_JOYSTICK_POWER_WIRED);
} else {
/* LSB of the battery nibble is used to report charging.
* The battery level is reported from 0(empty)-8(full)
*/
int level = (packet->controllerState.ucBatteryAndConnection & 0xE0) >> 4;
if (level == 0) {
SDL_SendJoystickBatteryLevel(joystick, SDL_JOYSTICK_POWER_EMPTY);
} else if (level <= 2) {
SDL_SendJoystickBatteryLevel(joystick, SDL_JOYSTICK_POWER_LOW);
} else if (level <= 6) {
SDL_SendJoystickBatteryLevel(joystick, SDL_JOYSTICK_POWER_MEDIUM);
} else {
SDL_SendJoystickBatteryLevel(joystick, SDL_JOYSTICK_POWER_FULL);
}
}
/* High nibble of battery/connection byte is battery level, low nibble is connection status
* LSB of connection nibble is USB/Switch connection status
* LSB of the battery nibble is used to report charging.
* The battery level is reported from 0(empty)-8(full)
*/
SDL_PowerState state;
int charging = (packet->controllerState.ucBatteryAndConnection & 0x10);
int level = (packet->controllerState.ucBatteryAndConnection & 0xE0) >> 4;
int percent = (int)SDL_roundf((level / 8.0f) * 100.0f);
if (packet->controllerState.ucBatteryAndConnection & 0x01) {
joystick->connection_state = SDL_JOYSTICK_CONNECTION_WIRED;
} else {
joystick->connection_state = SDL_JOYSTICK_CONNECTION_WIRELESS;
}
if (charging) {
if (level == 8) {
state = SDL_POWERSTATE_CHARGED;
} else {
state = SDL_POWERSTATE_CHARGING;
}
} else {
state = SDL_POWERSTATE_ON_BATTERY;
}
SDL_SendJoystickPowerInfo(joystick, state, percent);
if (ctx->m_bReportSensors) {
SDL_bool bHasSensorData = (packet->imuState[0].sAccelZ != 0 ||
packet->imuState[0].sAccelY != 0 ||

View File

@@ -493,15 +493,17 @@ static void DeactivateMotionPlus(SDL_DriverWii_Context *ctx)
static void UpdatePowerLevelWii(SDL_Joystick *joystick, Uint8 batteryLevelByte)
{
int percent;
if (batteryLevelByte > 178) {
SDL_SendJoystickBatteryLevel(joystick, SDL_JOYSTICK_POWER_FULL);
percent = 100;
} else if (batteryLevelByte > 51) {
SDL_SendJoystickBatteryLevel(joystick, SDL_JOYSTICK_POWER_MEDIUM);
percent = 70;
} else if (batteryLevelByte > 13) {
SDL_SendJoystickBatteryLevel(joystick, SDL_JOYSTICK_POWER_LOW);
percent = 20;
} else {
SDL_SendJoystickBatteryLevel(joystick, SDL_JOYSTICK_POWER_EMPTY);
percent = 5;
}
SDL_SendJoystickPowerInfo(joystick, SDL_POWERSTATE_ON_BATTERY, percent);
}
static void UpdatePowerLevelWiiU(SDL_Joystick *joystick, Uint8 extensionBatteryByte)
@@ -510,23 +512,39 @@ static void UpdatePowerLevelWiiU(SDL_Joystick *joystick, Uint8 extensionBatteryB
SDL_bool pluggedIn = !(extensionBatteryByte & 0x04);
Uint8 batteryLevel = extensionBatteryByte >> 4;
if (pluggedIn) {
joystick->connection_state = SDL_JOYSTICK_CONNECTION_WIRED;
} else {
joystick->connection_state = SDL_JOYSTICK_CONNECTION_WIRELESS;
}
/* Not sure if all Wii U Pro controllers act like this, but on mine
* 4, 3, and 2 are held for about 20 hours each
* 1 is held for about 6 hours
* 0 is held for about 2 hours
* No value above 4 has been observed.
*/
if (pluggedIn && !charging) {
SDL_SendJoystickBatteryLevel(joystick, SDL_JOYSTICK_POWER_WIRED);
} else if (batteryLevel >= 4) {
SDL_SendJoystickBatteryLevel(joystick, SDL_JOYSTICK_POWER_FULL);
} else if (batteryLevel > 1) {
SDL_SendJoystickBatteryLevel(joystick, SDL_JOYSTICK_POWER_MEDIUM);
} else if (batteryLevel == 1) {
SDL_SendJoystickBatteryLevel(joystick, SDL_JOYSTICK_POWER_LOW);
SDL_PowerState state;
int percent;
if (charging) {
state = SDL_POWERSTATE_CHARGING;
} else if (pluggedIn) {
state = SDL_POWERSTATE_CHARGED;
} else {
SDL_SendJoystickBatteryLevel(joystick, SDL_JOYSTICK_POWER_EMPTY);
state = SDL_POWERSTATE_ON_BATTERY;
}
if (batteryLevel >= 4) {
percent = 100;
} else if (batteryLevel == 3) {
percent = 70;
} else if (batteryLevel == 2) {
percent = 40;
} else if (batteryLevel == 1) {
percent = 10;
} else {
percent = 3;
}
SDL_SendJoystickPowerInfo(joystick, state, percent);
}
static EWiiInputReportIDs GetButtonPacketType(SDL_DriverWii_Context *ctx)

View File

@@ -191,7 +191,6 @@ static SDL_bool HIDAPI_DriverXbox360_OpenJoystick(SDL_HIDAPI_Device *device, SDL
joystick->nbuttons = 11;
joystick->naxes = SDL_GAMEPAD_AXIS_MAX;
joystick->nhats = 1;
joystick->epowerlevel = SDL_JOYSTICK_POWER_WIRED;
return SDL_TRUE;
}

View File

@@ -109,17 +109,8 @@ static void SDLCALL SDL_PlayerLEDHintChanged(void *userdata, const char *name, c
static void UpdatePowerLevel(SDL_Joystick *joystick, Uint8 level)
{
float normalized_level = (float)level / 255.0f;
if (normalized_level <= 0.05f) {
SDL_SendJoystickBatteryLevel(joystick, SDL_JOYSTICK_POWER_EMPTY);
} else if (normalized_level <= 0.20f) {
SDL_SendJoystickBatteryLevel(joystick, SDL_JOYSTICK_POWER_LOW);
} else if (normalized_level <= 0.70f) {
SDL_SendJoystickBatteryLevel(joystick, SDL_JOYSTICK_POWER_MEDIUM);
} else {
SDL_SendJoystickBatteryLevel(joystick, SDL_JOYSTICK_POWER_FULL);
}
int percent = (int)SDL_roundf((level / 255.0f) * 100.0f);
SDL_SendJoystickPowerInfo(joystick, SDL_POWERSTATE_ON_BATTERY, percent);
}
static SDL_bool HIDAPI_DriverXbox360W_InitDevice(SDL_HIDAPI_Device *device)
@@ -186,7 +177,6 @@ static SDL_bool HIDAPI_DriverXbox360W_OpenJoystick(SDL_HIDAPI_Device *device, SD
/* Initialize the joystick capabilities */
joystick->nbuttons = 15;
joystick->naxes = SDL_GAMEPAD_AXIS_MAX;
joystick->epowerlevel = SDL_JOYSTICK_POWER_UNKNOWN;
return SDL_TRUE;
}

View File

@@ -432,10 +432,6 @@ static SDL_bool HIDAPI_DriverXboxOne_OpenJoystick(SDL_HIDAPI_Device *device, SDL
joystick->naxes = SDL_GAMEPAD_AXIS_MAX;
joystick->nhats = 1;
if (!device->is_bluetooth) {
joystick->epowerlevel = SDL_JOYSTICK_POWER_WIRED;
}
SDL_AddHintCallback(SDL_HINT_JOYSTICK_HIDAPI_XBOX_ONE_HOME_LED,
SDL_HomeLEDHintChanged, ctx);
return SDL_TRUE;
@@ -1068,23 +1064,31 @@ static void HIDAPI_DriverXboxOneBluetooth_HandleBatteryPacket(SDL_Joystick *joys
{
Uint8 flags = data[1];
SDL_bool on_usb = (((flags & 0x0C) >> 2) == 0);
SDL_PowerState state;
int percent;
if (on_usb) {
/* Does this ever happen? */
SDL_SendJoystickBatteryLevel(joystick, SDL_JOYSTICK_POWER_WIRED);
} else {
switch ((flags & 0x03)) {
case 0:
SDL_SendJoystickBatteryLevel(joystick, SDL_JOYSTICK_POWER_LOW);
break;
case 1:
SDL_SendJoystickBatteryLevel(joystick, SDL_JOYSTICK_POWER_MEDIUM);
break;
default: /* 2, 3 */
SDL_SendJoystickBatteryLevel(joystick, SDL_JOYSTICK_POWER_FULL);
break;
}
// Mapped percentage value from:
// https://learn.microsoft.com/en-us/gaming/gdk/_content/gc/reference/input/gameinput/interfaces/igameinputdevice/methods/igameinputdevice_getbatterystate
switch (flags & 0x03) {
case 0:
percent = 10;
break;
case 1:
percent = 40;
break;
case 2:
percent = 70;
break;
case 3:
percent = 100;
break;
}
if (on_usb) {
state = SDL_POWERSTATE_CHARGING;
} else {
state = SDL_POWERSTATE_ON_BATTERY;
}
SDL_SendJoystickPowerInfo(joystick, state, percent);
}
static void HIDAPI_DriverXboxOne_HandleSerialIDPacket(SDL_DriverXboxOne_Context *ctx, const Uint8 *data, int size)

View File

@@ -1517,6 +1517,13 @@ static int HIDAPI_JoystickOpen(SDL_Joystick *joystick, int device_index)
return SDL_SetError("HIDAPI device disconnected while opening");
}
/* Set the default connection state, can be overridden below */
if (device->is_bluetooth) {
joystick->connection_state = SDL_JOYSTICK_CONNECTION_WIRELESS;
} else {
joystick->connection_state = SDL_JOYSTICK_CONNECTION_WIRED;
}
if (!device->driver->OpenJoystick(device, joystick)) {
/* The open failed, mark this device as disconnected and update devices */
HIDAPI_JoystickDisconnected(device, joystickID);