diff --git a/src/joystick/hidapi/SDL_hidapi_ps4.c b/src/joystick/hidapi/SDL_hidapi_ps4.c index e87b858730..8603c1ab35 100644 --- a/src/joystick/hidapi/SDL_hidapi_ps4.c +++ b/src/joystick/hidapi/SDL_hidapi_ps4.c @@ -33,10 +33,14 @@ #ifdef SDL_JOYSTICK_HIDAPI_PS4 /* Define this if you want to log all packets from the controller */ -/*#define DEBUG_PS4_PROTOCOL*/ +#if 0 +#define DEBUG_PS4_PROTOCOL +#endif /* Define this if you want to log calibration data */ -/*#define DEBUG_PS4_CALIBRATION*/ +#if 0 +#define DEBUG_PS4_CALIBRATION +#endif #define BLUETOOTH_DISCONNECT_TIMEOUT_MS 500 @@ -131,8 +135,10 @@ typedef struct SDL_bool touchpad_supported; SDL_bool effects_supported; SDL_bool enhanced_mode; + SDL_bool enhanced_mode_available; SDL_bool report_sensors; SDL_bool report_touchpad; + SDL_bool report_battery; SDL_bool hardware_calibration; IMUCalibrationData calibration[6]; Uint64 last_packet; @@ -627,10 +633,6 @@ static int HIDAPI_DriverPS4_UpdateEffects(SDL_HIDAPI_Device *device) SDL_DriverPS4_Context *ctx = (SDL_DriverPS4_Context *)device->context; DS4EffectsState_t effects; - if (!ctx->enhanced_mode || !ctx->effects_supported) { - return SDL_Unsupported(); - } - SDL_zero(effects); if (ctx->vibration_supported) { @@ -675,23 +677,42 @@ static void HIDAPI_DriverPS4_TickleBluetooth(SDL_HIDAPI_Device *device) } } -static void HIDAPI_DriverPS4_SetEnhancedMode(SDL_HIDAPI_Device *device, SDL_Joystick *joystick) +static void HIDAPI_DriverPS4_SetEnhancedModeAvailable(SDL_DriverPS4_Context *ctx) { - SDL_DriverPS4_Context *ctx = (SDL_DriverPS4_Context *)device->context; + if (!ctx->effects_supported) { + /* We shouldn't be sending any packets to the controller */ + return; + } - if (!ctx->enhanced_mode) { + ctx->enhanced_mode_available = SDL_TRUE; + + if (ctx->touchpad_supported) { + SDL_PrivateJoystickAddTouchpad(ctx->joystick, 2); + } + if (ctx->sensors_supported) { + SDL_PrivateJoystickAddSensor(ctx->joystick, SDL_SENSOR_GYRO, 250.0f); + SDL_PrivateJoystickAddSensor(ctx->joystick, SDL_SENSOR_ACCEL, 250.0f); + } +} + +static void HIDAPI_DriverPS4_SetEnhancedMode(SDL_DriverPS4_Context *ctx) +{ + if (!ctx->enhanced_mode_available) { + HIDAPI_DriverPS4_SetEnhancedModeAvailable(ctx); + } + + if (!ctx->enhanced_mode && ctx->enhanced_mode_available) { ctx->enhanced_mode = SDL_TRUE; if (ctx->touchpad_supported) { - SDL_PrivateJoystickAddTouchpad(joystick, 2); ctx->report_touchpad = SDL_TRUE; } - if (ctx->sensors_supported) { - SDL_PrivateJoystickAddSensor(joystick, SDL_SENSOR_GYRO, 250.0f); - SDL_PrivateJoystickAddSensor(joystick, SDL_SENSOR_ACCEL, 250.0f); + + if (ctx->device->is_bluetooth && ctx->official_controller) { + ctx->report_battery = SDL_TRUE; } - HIDAPI_DriverPS4_UpdateEffects(device); + HIDAPI_DriverPS4_UpdateEffects(ctx->device); } } @@ -699,9 +720,12 @@ static void SDLCALL SDL_PS4RumbleHintChanged(void *userdata, const char *name, c { SDL_DriverPS4_Context *ctx = (SDL_DriverPS4_Context *)userdata; - /* This is a one-way trip, you can't switch the controller back to simple report mode */ - if (SDL_GetStringBoolean(hint, SDL_FALSE)) { - HIDAPI_DriverPS4_SetEnhancedMode(ctx->device, ctx->joystick); + if (SDL_strcasecmp(hint, "auto") == 0) { + /* Mark the controller as enhanced mode capable, but wait for API calls to enable it */ + HIDAPI_DriverPS4_SetEnhancedModeAvailable(ctx); + } else if (SDL_GetStringBoolean(hint, SDL_FALSE)) { + /* This is a one-way trip, you can't switch the controller back to simple report mode */ + HIDAPI_DriverPS4_SetEnhancedMode(ctx); } } @@ -740,11 +764,9 @@ static SDL_bool HIDAPI_DriverPS4_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joy /* Initialize the joystick capabilities */ joystick->nbuttons = ctx->touchpad_supported ? 16 : 15; joystick->naxes = SDL_GAMEPAD_AXIS_MAX; - if (device->is_bluetooth && ctx->official_controller) { + if (device->is_bluetooth) { + /* We'll update this once we're in enhanced mode */ joystick->epowerlevel = SDL_JOYSTICK_POWER_UNKNOWN; - } else if (device->is_bluetooth) { - /* We can't get the power status, assume it's full */ - joystick->epowerlevel = SDL_JOYSTICK_POWER_FULL; } else { joystick->epowerlevel = SDL_JOYSTICK_POWER_WIRED; } @@ -752,7 +774,7 @@ static SDL_bool HIDAPI_DriverPS4_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joy if (ctx->enhanced_mode) { /* Force initialization when opening the joystick */ ctx->enhanced_mode = SDL_FALSE; - HIDAPI_DriverPS4_SetEnhancedMode(device, joystick); + HIDAPI_DriverPS4_SetEnhancedMode(ctx); } else { SDL_AddHintCallback(SDL_HINT_JOYSTICK_HIDAPI_PS4_RUMBLE, SDL_PS4RumbleHintChanged, ctx); @@ -784,7 +806,7 @@ static Uint32 HIDAPI_DriverPS4_GetJoystickCapabilities(SDL_HIDAPI_Device *device SDL_DriverPS4_Context *ctx = (SDL_DriverPS4_Context *)device->context; Uint32 result = 0; - if (ctx->enhanced_mode) { + if (ctx->enhanced_mode_available) { if (ctx->lightbar_supported) { result |= SDL_JOYCAP_LED; } @@ -818,12 +840,12 @@ static int HIDAPI_DriverPS4_SendJoystickEffect(SDL_HIDAPI_Device *device, SDL_Jo Uint8 data[78]; int report_size, offset; - if (!ctx->effects_supported) { + if (!ctx->enhanced_mode_available) { return SDL_Unsupported(); } if (!ctx->enhanced_mode) { - HIDAPI_DriverPS4_SetEnhancedMode(device, joystick); + HIDAPI_DriverPS4_SetEnhancedMode(ctx); } SDL_zeroa(data); @@ -864,11 +886,12 @@ static int HIDAPI_DriverPS4_SetJoystickSensorsEnabled(SDL_HIDAPI_Device *device, { SDL_DriverPS4_Context *ctx = (SDL_DriverPS4_Context *)device->context; - if (!ctx->enhanced_mode) { + if (!ctx->enhanced_mode_available) { return SDL_Unsupported(); } if (enabled) { + HIDAPI_DriverPS4_SetEnhancedMode(ctx); HIDAPI_DriverPS4_LoadCalibrationData(device); } ctx->report_sensors = enabled; @@ -980,21 +1003,17 @@ static void HIDAPI_DriverPS4_HandleStatePacket(SDL_Joystick *joystick, SDL_hid_d axis = ((int)packet->ucRightJoystickY * 257) - 32768; SDL_SendJoystickAxis(timestamp, joystick, SDL_GAMEPAD_AXIS_RIGHTY, axis); - if (size > 9 && ctx->device->is_bluetooth && ctx->official_controller) { - if (packet->ucBatteryLevel & 0x10) { - SDL_SendJoystickBatteryLevel(joystick, SDL_JOYSTICK_POWER_WIRED); + if (size > 9 && 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 { - /* 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_SendJoystickBatteryLevel(joystick, SDL_JOYSTICK_POWER_FULL); } } @@ -1149,7 +1168,7 @@ static SDL_bool HIDAPI_DriverPS4_UpdateDevice(SDL_HIDAPI_Device *device) case k_EPS4ReportIdBluetoothState9: if (!ctx->enhanced_mode) { /* This is the extended report, we can enable effects now */ - HIDAPI_DriverPS4_SetEnhancedMode(device, joystick); + HIDAPI_DriverPS4_SetEnhancedMode(ctx); } /* Bluetooth state packets have two additional bytes at the beginning, the first notes if HID is present */ HIDAPI_DriverPS4_HandleStatePacket(joystick, device->dev, ctx, (PS4StatePacket_t *)&data[3], size - 3); diff --git a/src/joystick/hidapi/SDL_hidapi_ps5.c b/src/joystick/hidapi/SDL_hidapi_ps5.c index a116c8fb7b..1f96d1a00f 100644 --- a/src/joystick/hidapi/SDL_hidapi_ps5.c +++ b/src/joystick/hidapi/SDL_hidapi_ps5.c @@ -30,10 +30,14 @@ #ifdef SDL_JOYSTICK_HIDAPI_PS5 /* Define this if you want to log all packets from the controller */ -/*#define DEBUG_PS5_PROTOCOL*/ +#if 0 +#define DEBUG_PS5_PROTOCOL +#endif /* Define this if you want to log calibration data */ -/*#define DEBUG_PS5_CALIBRATION*/ +#if 0 +#define DEBUG_PS5_CALIBRATION +#endif #define GYRO_RES_PER_DEGREE 1024.0f #define ACCEL_RES_PER_G 8192.0f @@ -219,8 +223,10 @@ typedef struct SDL_bool touchpad_supported; SDL_bool effects_supported; SDL_bool enhanced_mode; + SDL_bool enhanced_mode_available; SDL_bool report_sensors; SDL_bool report_touchpad; + SDL_bool report_battery; SDL_bool hardware_calibration; IMUCalibrationData calibration[6]; Uint16 firmware_version; @@ -388,13 +394,17 @@ static SDL_bool HIDAPI_DriverPS5_InitDevice(SDL_HIDAPI_Device *device) ctx->enhanced_mode = SDL_TRUE; } else { /* Connected over Bluetooth, using simple reports (DirectInput enabled) */ - - /* Games written prior the introduction of PS5 controller support in SDL will not be aware of - SDL_HINT_JOYSTICK_HIDAPI_PS5_RUMBLE, but they did know SDL_HINT_JOYSTICK_HIDAPI_PS4_RUMBLE. - To support apps that only knew about the PS4 hint, we'll use the PS4 hint as the default. - */ - ctx->enhanced_mode = SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI_PS5_RUMBLE, - SDL_GetHintBoolean(SDL_HINT_JOYSTICK_HIDAPI_PS4_RUMBLE, SDL_FALSE)); + const char *hint = SDL_GetHint(SDL_HINT_JOYSTICK_HIDAPI_PS5_RUMBLE); + if (!hint) { + /* Games written prior the introduction of PS5 controller support in SDL will not be aware of + SDL_HINT_JOYSTICK_HIDAPI_PS5_RUMBLE, but they did know SDL_HINT_JOYSTICK_HIDAPI_PS4_RUMBLE. + To support apps that only knew about the PS4 hint, we'll use the PS4 hint as the default. + */ + hint = SDL_GetHint(SDL_HINT_JOYSTICK_HIDAPI_PS4_RUMBLE); + } + if (hint && SDL_strcasecmp(hint, "auto") != 0) { + ctx->enhanced_mode = SDL_GetStringBoolean(hint, SDL_FALSE); + } } if (ctx->enhanced_mode) { @@ -630,14 +640,8 @@ static int HIDAPI_DriverPS5_UpdateEffects(SDL_HIDAPI_Device *device, int effect_ SDL_DriverPS5_Context *ctx = (SDL_DriverPS5_Context *)device->context; DS5EffectsState_t effects; - if (!ctx->enhanced_mode || !ctx->effects_supported) { - return SDL_Unsupported(); - } - - SDL_zero(effects); - /* Make sure the Bluetooth connection sequence has completed before sending LED color change */ - if (device->is_bluetooth && + if (device->is_bluetooth && ctx->enhanced_mode && (effect_mask & (k_EDS5EffectLED | k_EDS5EffectPadLights)) != 0) { if (ctx->led_reset_state != k_EDS5LEDResetStateComplete) { ctx->led_reset_state = k_EDS5LEDResetStatePending; @@ -645,6 +649,8 @@ static int HIDAPI_DriverPS5_UpdateEffects(SDL_HIDAPI_Device *device, int effect_ } } + SDL_zero(effects); + if (ctx->vibration_supported) { if (ctx->rumble_left || ctx->rumble_right) { if (ctx->firmware_version < 0x0224) { @@ -763,33 +769,52 @@ static void HIDAPI_DriverPS5_TickleBluetooth(SDL_HIDAPI_Device *device) } } -static void HIDAPI_DriverPS5_SetEnhancedMode(SDL_HIDAPI_Device *device, SDL_Joystick *joystick) +static void HIDAPI_DriverPS5_SetEnhancedModeAvailable(SDL_DriverPS5_Context *ctx) { - SDL_DriverPS5_Context *ctx = (SDL_DriverPS5_Context *)device->context; + if (!ctx->effects_supported) { + /* We shouldn't be sending any packets to the controller */ + return; + } - if (!ctx->enhanced_mode) { + ctx->enhanced_mode_available = SDL_TRUE; + + if (ctx->touchpad_supported) { + SDL_PrivateJoystickAddTouchpad(ctx->joystick, 2); + } + if (ctx->sensors_supported) { + if (ctx->device->is_bluetooth) { + /* Bluetooth sensor update rate appears to be 1000 Hz */ + SDL_PrivateJoystickAddSensor(ctx->joystick, SDL_SENSOR_GYRO, 1000.0f); + SDL_PrivateJoystickAddSensor(ctx->joystick, SDL_SENSOR_ACCEL, 1000.0f); + } else { + SDL_PrivateJoystickAddSensor(ctx->joystick, SDL_SENSOR_GYRO, 250.0f); + SDL_PrivateJoystickAddSensor(ctx->joystick, SDL_SENSOR_ACCEL, 250.0f); + } + } +} + +static void HIDAPI_DriverPS5_SetEnhancedMode(SDL_DriverPS5_Context *ctx) +{ + if (!ctx->enhanced_mode_available) { + HIDAPI_DriverPS5_SetEnhancedModeAvailable(ctx); + } + + if (!ctx->enhanced_mode && ctx->enhanced_mode_available) { ctx->enhanced_mode = SDL_TRUE; if (ctx->touchpad_supported) { - SDL_PrivateJoystickAddTouchpad(joystick, 2); ctx->report_touchpad = SDL_TRUE; } - if (ctx->sensors_supported) { - if (device->is_bluetooth) { - /* Bluetooth sensor update rate appears to be 1000 Hz */ - SDL_PrivateJoystickAddSensor(joystick, SDL_SENSOR_GYRO, 1000.0f); - SDL_PrivateJoystickAddSensor(joystick, SDL_SENSOR_ACCEL, 1000.0f); - } else { - SDL_PrivateJoystickAddSensor(joystick, SDL_SENSOR_GYRO, 250.0f); - SDL_PrivateJoystickAddSensor(joystick, SDL_SENSOR_ACCEL, 250.0f); - } + + if (ctx->device->is_bluetooth) { + ctx->report_battery = SDL_TRUE; } /* Switch into enhanced report mode */ - HIDAPI_DriverPS5_UpdateEffects(device, 0); + HIDAPI_DriverPS5_UpdateEffects(ctx->device, 0); /* Update the light effects */ - HIDAPI_DriverPS5_UpdateEffects(device, (k_EDS5EffectLED | k_EDS5EffectPadLights)); + HIDAPI_DriverPS5_UpdateEffects(ctx->device, (k_EDS5EffectLED | k_EDS5EffectPadLights)); } } @@ -797,9 +822,12 @@ static void SDLCALL SDL_PS5RumbleHintChanged(void *userdata, const char *name, c { SDL_DriverPS5_Context *ctx = (SDL_DriverPS5_Context *)userdata; - /* This is a one-way trip, you can't switch the controller back to simple report mode */ - if (SDL_GetStringBoolean(hint, SDL_FALSE)) { - HIDAPI_DriverPS5_SetEnhancedMode(ctx->device, ctx->joystick); + if (SDL_strcasecmp(hint, "auto") == 0) { + /* Mark the controller as enhanced mode capable, but wait for API calls to enable it */ + HIDAPI_DriverPS5_SetEnhancedModeAvailable(ctx); + } else if (SDL_GetStringBoolean(hint, SDL_FALSE)) { + /* This is a one-way trip, you can't switch the controller back to simple report mode */ + HIDAPI_DriverPS5_SetEnhancedMode(ctx); } } @@ -858,13 +886,18 @@ static SDL_bool HIDAPI_DriverPS5_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joy joystick->nbuttons = 15; } joystick->naxes = SDL_GAMEPAD_AXIS_MAX; - joystick->epowerlevel = device->is_bluetooth ? SDL_JOYSTICK_POWER_UNKNOWN : SDL_JOYSTICK_POWER_WIRED; + 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; if (ctx->enhanced_mode) { /* Force initialization when opening the joystick */ ctx->enhanced_mode = SDL_FALSE; - HIDAPI_DriverPS5_SetEnhancedMode(device, joystick); + HIDAPI_DriverPS5_SetEnhancedMode(ctx); } else { SDL_AddHintCallback(SDL_HINT_JOYSTICK_HIDAPI_PS5_RUMBLE, SDL_PS5RumbleHintChanged, ctx); @@ -903,7 +936,7 @@ static Uint32 HIDAPI_DriverPS5_GetJoystickCapabilities(SDL_HIDAPI_Device *device SDL_DriverPS5_Context *ctx = (SDL_DriverPS5_Context *)device->context; Uint32 result = 0; - if (ctx->enhanced_mode) { + if (ctx->enhanced_mode_available) { if (ctx->lightbar_supported) { result |= SDL_JOYCAP_LED; } @@ -940,12 +973,12 @@ static int HIDAPI_DriverPS5_SendJoystickEffect(SDL_HIDAPI_Device *device, SDL_Jo int *pending_size; int maximum_size; - if (!ctx->effects_supported) { + if (!ctx->enhanced_mode_available) { return SDL_Unsupported(); } if (!ctx->enhanced_mode) { - HIDAPI_DriverPS5_SetEnhancedMode(device, joystick); + HIDAPI_DriverPS5_SetEnhancedMode(ctx); } SDL_zeroa(data); @@ -1003,11 +1036,12 @@ static int HIDAPI_DriverPS5_SetJoystickSensorsEnabled(SDL_HIDAPI_Device *device, { SDL_DriverPS5_Context *ctx = (SDL_DriverPS5_Context *)device->context; - if (!ctx->enhanced_mode) { + if (!ctx->enhanced_mode_available) { return SDL_Unsupported(); } if (enabled) { + HIDAPI_DriverPS5_SetEnhancedMode(ctx); HIDAPI_DriverPS5_LoadCalibrationData(device); } ctx->report_sensors = enabled; @@ -1274,13 +1308,7 @@ static void HIDAPI_DriverPS5_HandleStatePacket(SDL_Joystick *joystick, SDL_hid_d SDL_SendJoystickTouchpad(timestamp, joystick, 0, 1, touchpad_state, touchpad_x * TOUCHPAD_SCALEX, touchpad_y * TOUCHPAD_SCALEY, touchpad_state ? 1.0f : 0.0f); } - /* A check of packet->ucBatteryLevel & 0x10 should work as a check for BT vs USB but doesn't - * seem to always work. Possibly related to being 100% charged? - */ - if (!ctx->device->is_bluetooth) { - /* 0x20 set means fully charged */ - SDL_SendJoystickBatteryLevel(joystick, SDL_JOYSTICK_POWER_WIRED); - } else { + if (ctx->report_battery) { /* Battery level ranges from 0 to 10 */ int level = (packet->ucBatteryLevel & 0xF); if (level == 0) { @@ -1396,7 +1424,7 @@ static SDL_bool HIDAPI_DriverPS5_UpdateDevice(SDL_HIDAPI_Device *device) case k_EPS5ReportIdBluetoothState: if (!ctx->enhanced_mode) { /* This is the extended report, we can enable effects now */ - HIDAPI_DriverPS5_SetEnhancedMode(device, joystick); + HIDAPI_DriverPS5_SetEnhancedMode(ctx); } HIDAPI_DriverPS5_HandleStatePacketCommon(joystick, device->dev, ctx, (PS5StatePacketCommon_t *)&data[2]); if (ctx->use_alternate_report) {