Просмотр исходного кода

joystick: Fix conflicting rumble and LED on Sony PS4 gamepads

(cherry picked from commit 189877b1d365986862564f725e74a5ab3fbed8b6)
Cameron Gutman 1 день назад
Родитель
Сommit
6dbdb94cae
1 измененных файлов с 38 добавлено и 12 удалено
  1. 38 12
      src/joystick/hidapi/SDL_hidapi_ps4.c

+ 38 - 12
src/joystick/hidapi/SDL_hidapi_ps4.c

@@ -117,6 +117,14 @@ typedef struct
     Uint8 ucVolumeSpeaker;
 } DS4EffectsState_t;
 
+// These enum values match with the validity flags in the PS4 output report
+typedef enum
+{
+    k_EPS4EffectRumble = (1 << 0),
+    k_EPS4EffectLED = (1 << 1),
+    k_EPS4EffectLEDBlink = (1 << 2),
+} EPS4Effect;
+
 typedef struct
 {
     Sint16 bias;
@@ -178,7 +186,7 @@ typedef struct
     PS4StatePacket_t last_state;
 } SDL_DriverPS4_Context;
 
-static bool HIDAPI_DriverPS4_InternalSendJoystickEffect(SDL_DriverPS4_Context *ctx, const void *effect, int size, bool application_usage);
+static bool HIDAPI_DriverPS4_InternalSendJoystickEffect(SDL_DriverPS4_Context *ctx, const void *effect, int size, EPS4Effect mask, bool application_usage);
 
 static void HIDAPI_DriverPS4_RegisterHints(SDL_HintCallback callback, void *userdata)
 {
@@ -661,7 +669,7 @@ static float HIDAPI_DriverPS4_ApplyCalibrationData(SDL_DriverPS4_Context *ctx, i
     return ((float)value - calibration->bias) * calibration->scale;
 }
 
-static bool HIDAPI_DriverPS4_UpdateEffects(SDL_DriverPS4_Context *ctx, bool application_usage)
+static bool HIDAPI_DriverPS4_UpdateEffects(SDL_DriverPS4_Context *ctx, EPS4Effect mask, bool application_usage)
 {
     DS4EffectsState_t effects;
 
@@ -682,7 +690,7 @@ static bool HIDAPI_DriverPS4_UpdateEffects(SDL_DriverPS4_Context *ctx, bool appl
             SetLedsForPlayerIndex(&effects, ctx->player_index);
         }
     }
-    return HIDAPI_DriverPS4_InternalSendJoystickEffect(ctx, &effects, sizeof(effects), application_usage);
+    return HIDAPI_DriverPS4_InternalSendJoystickEffect(ctx, &effects, sizeof(effects), mask, application_usage);
 }
 
 static void HIDAPI_DriverPS4_TickleBluetooth(SDL_HIDAPI_Device *device)
@@ -749,7 +757,7 @@ static void HIDAPI_DriverPS4_SetEnhancedMode(SDL_DriverPS4_Context *ctx)
         ctx->enhanced_mode = true;
 
         // Switch into enhanced report mode
-        HIDAPI_DriverPS4_UpdateEffects(ctx, false);
+        HIDAPI_DriverPS4_UpdateEffects(ctx, 0, false);
     }
 }
 
@@ -825,7 +833,7 @@ static void SDLCALL SDL_PS4ReportIntervalHintChanged(void *userdata, const char
     if (new_report_interval != ctx->report_interval) {
         ctx->report_interval = (Uint8)new_report_interval;
 
-        HIDAPI_DriverPS4_UpdateEffects(ctx, false);
+        HIDAPI_DriverPS4_UpdateEffects(ctx, 0, false);
         SDL_LockJoysticks();
         SDL_PrivateJoystickSensorRate(ctx->joystick, SDL_SENSOR_GYRO, (float)(1000 / ctx->report_interval));
         SDL_PrivateJoystickSensorRate(ctx->joystick, SDL_SENSOR_ACCEL, (float)(1000 / ctx->report_interval));
@@ -845,7 +853,7 @@ static void HIDAPI_DriverPS4_SetDevicePlayerIndex(SDL_HIDAPI_Device *device, SDL
 
     // This will set the new LED state based on the new player index
     // SDL automatically calls this, so it doesn't count as an application action to enable enhanced mode
-    HIDAPI_DriverPS4_UpdateEffects(ctx, false);
+    HIDAPI_DriverPS4_UpdateEffects(ctx, k_EPS4EffectLED, false);
 }
 
 static bool HIDAPI_DriverPS4_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
@@ -892,7 +900,7 @@ static bool HIDAPI_DriverPS4_RumbleJoystick(SDL_HIDAPI_Device *device, SDL_Joyst
     ctx->rumble_left = (low_frequency_rumble >> 8);
     ctx->rumble_right = (high_frequency_rumble >> 8);
 
-    return HIDAPI_DriverPS4_UpdateEffects(ctx, true);
+    return HIDAPI_DriverPS4_UpdateEffects(ctx, k_EPS4EffectRumble, true);
 }
 
 static bool HIDAPI_DriverPS4_RumbleJoystickTriggers(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, Uint16 left_rumble, Uint16 right_rumble)
@@ -930,10 +938,10 @@ static bool HIDAPI_DriverPS4_SetJoystickLED(SDL_HIDAPI_Device *device, SDL_Joyst
     ctx->led_green = green;
     ctx->led_blue = blue;
 
-    return HIDAPI_DriverPS4_UpdateEffects(ctx, true);
+    return HIDAPI_DriverPS4_UpdateEffects(ctx, k_EPS4EffectLED, true);
 }
 
-static bool HIDAPI_DriverPS4_InternalSendJoystickEffect(SDL_DriverPS4_Context *ctx, const void *effect, int size, bool application_usage)
+static bool HIDAPI_DriverPS4_InternalSendJoystickEffect(SDL_DriverPS4_Context *ctx, const void *effect, int size, EPS4Effect mask, bool application_usage)
 {
     Uint8 data[78];
     int report_size, offset;
@@ -959,13 +967,21 @@ static bool HIDAPI_DriverPS4_InternalSendJoystickEffect(SDL_DriverPS4_Context *c
     if (ctx->device->is_bluetooth && ctx->official_controller) {
         data[0] = k_EPS4ReportIdBluetoothEffects;
         data[1] = 0xC0 | ctx->report_interval; // Magic value HID + CRC, also sets update interval
-        data[3] = 0x03;        // 0x1 is rumble, 0x2 is lightbar, 0x4 is the blink interval
+
+        // Some third-party PS4 gamepads expect to receive both rumble and LED together,
+        // so we will only send them separately to official Sony gamepads. Unfortunately,
+        // this means that we'll fight with other apps that might be controlling one or
+        // the other separately for third-party PS4 gamepads. We can add a whitelist for
+        // compatible third-party gamepads later if we want.
+        data[3] = (Uint8)(ctx->official_controller ? mask : (k_EPS4EffectRumble | k_EPS4EffectLED));
 
         report_size = 78;
         offset = 6;
     } else {
         data[0] = k_EPS4ReportIdUsbEffects;
-        data[1] = 0x07; // Magic value
+
+        // FIXME: Should we send a consistent default effect mask between BT and USB?
+        data[1] = (Uint8)(ctx->official_controller ? mask : (k_EPS4EffectRumble | k_EPS4EffectLED | k_EPS4EffectLEDBlink));
 
         report_size = 32;
         offset = 4;
@@ -991,8 +1007,18 @@ static bool HIDAPI_DriverPS4_InternalSendJoystickEffect(SDL_DriverPS4_Context *c
 static bool HIDAPI_DriverPS4_SendJoystickEffect(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, const void *effect, int size)
 {
     SDL_DriverPS4_Context *ctx = (SDL_DriverPS4_Context *)device->context;
+    EPS4Effect mask;
+
+    // Unlike the PS5 driver, the PS4 driver does not expect the validity bits as part of
+    // the provided effect data. The default values we provide also diverge for different
+    // gamepads and connection mediums. We should probably clean this up eventually.
+    if (ctx->device->is_bluetooth && ctx->official_controller) {
+        mask = k_EPS4EffectRumble | k_EPS4EffectLED;
+    } else {
+        mask = k_EPS4EffectRumble | k_EPS4EffectLED | k_EPS4EffectLEDBlink;
+    }
 
-    return HIDAPI_DriverPS4_InternalSendJoystickEffect(ctx, effect, size, true);
+    return HIDAPI_DriverPS4_InternalSendJoystickEffect(ctx, effect, size, mask, true);
 }
 
 static bool HIDAPI_DriverPS4_SetJoystickSensorsEnabled(SDL_HIDAPI_Device *device, SDL_Joystick *joystick, bool enabled)