Commit afccabb88183a0c7c3513223b1e026fe16233819

Cameron Gutman 2021-11-11T12:12:05

joystick: Add capability flags for rumble and trigger rumble When API limitations force us to guess, we favor a false positive (reporting support when it doesn't exist) compared to a false negative.

diff --git a/src/joystick/SDL_sysjoystick.h b/src/joystick/SDL_sysjoystick.h
index e897c44..dff6b36 100644
--- a/src/joystick/SDL_sysjoystick.h
+++ b/src/joystick/SDL_sysjoystick.h
@@ -123,6 +123,8 @@ struct _SDL_Joystick
 
 /* Joystick capability flags for GetCapabilities() */
 #define SDL_JOYCAP_LED              0x01
+#define SDL_JOYCAP_RUMBLE           0x02
+#define SDL_JOYCAP_RUMBLE_TRIGGERS  0x04
 
 /* Macro to combine a USB vendor ID and product ID into a single Uint32 value */
 #define MAKE_VIDPID(VID, PID)   (((Uint32)(VID))<<16|(PID))
diff --git a/src/joystick/darwin/SDL_iokitjoystick.c b/src/joystick/darwin/SDL_iokitjoystick.c
index ee02891..1d5b80a 100644
--- a/src/joystick/darwin/SDL_iokitjoystick.c
+++ b/src/joystick/darwin/SDL_iokitjoystick.c
@@ -942,7 +942,18 @@ DARWIN_JoystickRumbleTriggers(SDL_Joystick *joystick, Uint16 left_rumble, Uint16
 static Uint32
 DARWIN_JoystickGetCapabilities(SDL_Joystick *joystick)
 {
-    return 0;
+    recDevice *device = joystick->hwdata;
+    Uint32 result = 0;
+
+    if (!device) {
+        return 0;
+    }
+
+    if (device->ffservice) {
+        result |= SDL_JOYCAP_RUMBLE;
+    }
+
+    return result;
 }
 
 static int
diff --git a/src/joystick/hidapi/SDL_hidapi_gamecube.c b/src/joystick/hidapi/SDL_hidapi_gamecube.c
index 9fc3ff7..2b6f7b9 100644
--- a/src/joystick/hidapi/SDL_hidapi_gamecube.c
+++ b/src/joystick/hidapi/SDL_hidapi_gamecube.c
@@ -467,7 +467,23 @@ HIDAPI_DriverGameCube_RumbleJoystickTriggers(SDL_HIDAPI_Device *device, SDL_Joys
 static Uint32
 HIDAPI_DriverGameCube_GetJoystickCapabilities(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
 {
-    return 0;
+    SDL_DriverGameCube_Context *ctx = (SDL_DriverGameCube_Context *)device->context;
+    Uint32 result = 0;
+
+    if (!ctx->pc_mode) {
+        Uint8 i;
+
+        for (i = 0; i < MAX_CONTROLLERS; i += 1) {
+            if (joystick->instance_id == ctx->joysticks[i]) {
+                if (!ctx->wireless[i] && ctx->rumbleAllowed[i]) {
+                    result |= SDL_JOYCAP_RUMBLE;
+                    break;
+                }
+            }
+        }
+    }
+
+    return result;
 }
 
 static int
diff --git a/src/joystick/hidapi/SDL_hidapi_luna.c b/src/joystick/hidapi/SDL_hidapi_luna.c
index 48de69f..90d50d4 100644
--- a/src/joystick/hidapi/SDL_hidapi_luna.c
+++ b/src/joystick/hidapi/SDL_hidapi_luna.c
@@ -135,7 +135,13 @@ HIDAPI_DriverLuna_RumbleJoystickTriggers(SDL_HIDAPI_Device *device, SDL_Joystick
 static Uint32
 HIDAPI_DriverLuna_GetJoystickCapabilities(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
 {
-    return 0;
+    Uint32 result = 0;
+
+    if (device->product_id == BLUETOOTH_PRODUCT_LUNA_CONTROLLER) {
+        result |= SDL_JOYCAP_RUMBLE;
+    }
+
+    return result;
 }
 
 static int
diff --git a/src/joystick/hidapi/SDL_hidapi_ps4.c b/src/joystick/hidapi/SDL_hidapi_ps4.c
index 7bd2434..b6df375 100644
--- a/src/joystick/hidapi/SDL_hidapi_ps4.c
+++ b/src/joystick/hidapi/SDL_hidapi_ps4.c
@@ -601,7 +601,14 @@ HIDAPI_DriverPS4_RumbleJoystickTriggers(SDL_HIDAPI_Device *device, SDL_Joystick 
 static Uint32
 HIDAPI_DriverPS4_GetJoystickCapabilities(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
 {
-    return SDL_JOYCAP_LED;
+    SDL_DriverPS4_Context *ctx = (SDL_DriverPS4_Context *)device->context;
+    Uint32 result = 0;
+
+    if (ctx->enhanced_mode && ctx->effects_supported) {
+        result |= SDL_JOYCAP_LED | SDL_JOYCAP_RUMBLE;
+    }
+
+    return result;
 }
 
 static int
diff --git a/src/joystick/hidapi/SDL_hidapi_ps5.c b/src/joystick/hidapi/SDL_hidapi_ps5.c
index ff8fc66..c52c6df 100644
--- a/src/joystick/hidapi/SDL_hidapi_ps5.c
+++ b/src/joystick/hidapi/SDL_hidapi_ps5.c
@@ -666,7 +666,14 @@ HIDAPI_DriverPS5_RumbleJoystickTriggers(SDL_HIDAPI_Device *device, SDL_Joystick 
 static Uint32
 HIDAPI_DriverPS5_GetJoystickCapabilities(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
 {
-    return SDL_JOYCAP_LED;
+    SDL_DriverPS5_Context *ctx = (SDL_DriverPS5_Context *)device->context;
+    Uint32 result = 0;
+
+    if (ctx->enhanced_mode) {
+        result |= SDL_JOYCAP_LED | SDL_JOYCAP_RUMBLE;
+    }
+
+    return result;
 }
 
 static int
diff --git a/src/joystick/hidapi/SDL_hidapi_stadia.c b/src/joystick/hidapi/SDL_hidapi_stadia.c
index f6ddd35..0df4315 100644
--- a/src/joystick/hidapi/SDL_hidapi_stadia.c
+++ b/src/joystick/hidapi/SDL_hidapi_stadia.c
@@ -129,7 +129,7 @@ HIDAPI_DriverStadia_RumbleJoystickTriggers(SDL_HIDAPI_Device *device, SDL_Joysti
 static Uint32
 HIDAPI_DriverStadia_GetJoystickCapabilities(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
 {
-    return 0;
+    return SDL_JOYCAP_RUMBLE;
 }
 
 static int
diff --git a/src/joystick/hidapi/SDL_hidapi_switch.c b/src/joystick/hidapi/SDL_hidapi_switch.c
index a12aa26..9341d1d 100644
--- a/src/joystick/hidapi/SDL_hidapi_switch.c
+++ b/src/joystick/hidapi/SDL_hidapi_switch.c
@@ -1109,8 +1109,15 @@ HIDAPI_DriverSwitch_RumbleJoystickTriggers(SDL_HIDAPI_Device *device, SDL_Joysti
 static Uint32
 HIDAPI_DriverSwitch_GetJoystickCapabilities(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
 {
-    /* Doesn't have an RGB LED, so don't return SDL_JOYCAP_LED here */
-    return 0;
+    SDL_DriverSwitch_Context *ctx = (SDL_DriverSwitch_Context *)device->context;
+    Uint32 result = 0;
+
+    if (!ctx->m_bInputOnly) {
+        /* Doesn't have an RGB LED, so don't return SDL_JOYCAP_LED here */
+        result |= SDL_JOYCAP_RUMBLE;
+    }
+
+    return result;
 }
 
 static int
diff --git a/src/joystick/hidapi/SDL_hidapi_xbox360.c b/src/joystick/hidapi/SDL_hidapi_xbox360.c
index 13911ff..39cbbfa 100644
--- a/src/joystick/hidapi/SDL_hidapi_xbox360.c
+++ b/src/joystick/hidapi/SDL_hidapi_xbox360.c
@@ -207,7 +207,7 @@ static Uint32
 HIDAPI_DriverXbox360_GetJoystickCapabilities(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
 {
     /* Doesn't have an RGB LED, so don't return SDL_JOYCAP_LED here */
-    return 0;
+    return SDL_JOYCAP_RUMBLE;
 }
 
 static int
diff --git a/src/joystick/hidapi/SDL_hidapi_xbox360w.c b/src/joystick/hidapi/SDL_hidapi_xbox360w.c
index 9603e32..ae8bbdc 100644
--- a/src/joystick/hidapi/SDL_hidapi_xbox360w.c
+++ b/src/joystick/hidapi/SDL_hidapi_xbox360w.c
@@ -177,7 +177,7 @@ static Uint32
 HIDAPI_DriverXbox360W_GetJoystickCapabilities(SDL_HIDAPI_Device *device, SDL_Joystick *joystick)
 {
     /* Doesn't have an RGB LED, so don't return SDL_JOYCAP_LED here */
-    return 0;
+    return SDL_JOYCAP_RUMBLE;
 }
 
 static int
diff --git a/src/joystick/hidapi/SDL_hidapi_xboxone.c b/src/joystick/hidapi/SDL_hidapi_xboxone.c
index a967405..097deac 100644
--- a/src/joystick/hidapi/SDL_hidapi_xboxone.c
+++ b/src/joystick/hidapi/SDL_hidapi_xboxone.c
@@ -428,6 +428,11 @@ HIDAPI_DriverXboxOne_GetJoystickCapabilities(SDL_HIDAPI_Device *device, SDL_Joys
     SDL_DriverXboxOne_Context *ctx = (SDL_DriverXboxOne_Context *)device->context;
     Uint32 result = 0;
 
+    result |= SDL_JOYCAP_RUMBLE;
+    if (ctx->has_trigger_rumble) {
+        result |= SDL_JOYCAP_RUMBLE_TRIGGERS;
+    }
+
     if (ctx->has_color_led) {
         result |= SDL_JOYCAP_LED;
     }
diff --git a/src/joystick/iphoneos/SDL_mfijoystick.m b/src/joystick/iphoneos/SDL_mfijoystick.m
index e15df71..08ed418 100644
--- a/src/joystick/iphoneos/SDL_mfijoystick.m
+++ b/src/joystick/iphoneos/SDL_mfijoystick.m
@@ -1331,7 +1331,6 @@ IOS_JoystickGetCapabilities(SDL_Joystick *joystick)
 {
     Uint32 result = 0;
 
-#ifdef ENABLE_MFI_LIGHT
     @autoreleasepool {
         SDL_JoystickDeviceItem *device = joystick->hwdata;
 
@@ -1341,13 +1340,25 @@ IOS_JoystickGetCapabilities(SDL_Joystick *joystick)
 
         if (@available(macos 11.0, iOS 14.0, tvOS 14.0, *)) {
             GCController *controller = device->controller;
-            GCDeviceLight *light = controller.light;
-            if (light) {
+#ifdef ENABLE_MFI_LIGHT
+            if (controller.light) {
                 result |= SDL_JOYCAP_LED;
             }
+#endif /* ENABLE_MFI_LIGHT */
+
+#ifdef ENABLE_MFI_RUMBLE
+            if (controller.haptics) {
+                for (GCHapticsLocality locality in controller.haptics.supportedLocalities) {
+                    if ([locality isEqualToString:GCHapticsLocalityHandles]) {
+                        result |= SDL_JOYCAP_RUMBLE;
+                    } else if ([locality isEqualToString:GCHapticsLocalityTriggers]) {
+                        result |= SDL_JOYCAP_RUMBLE_TRIGGERS;
+                    }
+                }
+            }
+#endif /* ENABLE_MFI_RUMBLE */
         }
     }
-#endif /* ENABLE_MFI_LIGHT */
 
     return result;
 }
diff --git a/src/joystick/linux/SDL_sysjoystick.c b/src/joystick/linux/SDL_sysjoystick.c
index f049248..56019cc 100644
--- a/src/joystick/linux/SDL_sysjoystick.c
+++ b/src/joystick/linux/SDL_sysjoystick.c
@@ -1193,7 +1193,13 @@ LINUX_JoystickRumbleTriggers(SDL_Joystick *joystick, Uint16 left_rumble, Uint16 
 static Uint32
 LINUX_JoystickGetCapabilities(SDL_Joystick *joystick)
 {
-    return 0;
+    Uint32 result = 0;
+
+    if (joystick->hwdata->ff_rumble || joystick->hwdata->ff_sine) {
+        result |= SDL_JOYCAP_RUMBLE;
+    }
+
+    return result;
 }
 
 static int
diff --git a/src/joystick/vita/SDL_sysjoystick.c b/src/joystick/vita/SDL_sysjoystick.c
index 1d80207..387679f 100644
--- a/src/joystick/vita/SDL_sysjoystick.c
+++ b/src/joystick/vita/SDL_sysjoystick.c
@@ -368,8 +368,8 @@ VITA_JoystickRumbleTriggers(SDL_Joystick *joystick, Uint16 left, Uint16 right)
 static Uint32
 VITA_JoystickGetCapabilities(SDL_Joystick *joystick)
 {
-    // always return LED supported for now
-    return SDL_JOYCAP_LED;
+    // always return LED and rumble supported for now
+    return SDL_JOYCAP_LED | SDL_JOYCAP_RUMBLE;
 }
 
 
diff --git a/src/joystick/windows/SDL_dinputjoystick.c b/src/joystick/windows/SDL_dinputjoystick.c
index 43a5b49..16deb51 100644
--- a/src/joystick/windows/SDL_dinputjoystick.c
+++ b/src/joystick/windows/SDL_dinputjoystick.c
@@ -927,7 +927,13 @@ SDL_DINPUT_JoystickRumble(SDL_Joystick * joystick, Uint16 low_frequency_rumble, 
 Uint32
 SDL_DINPUT_JoystickGetCapabilities(SDL_Joystick * joystick)
 {
-    return 0;
+    Uint32 result = 0;
+
+    if (joystick->hwdata->Capabilities.dwFlags & DIDC_FORCEFEEDBACK) {
+        result |= SDL_JOYCAP_RUMBLE;
+    }
+
+    return result;
 }
 
 static Uint8
diff --git a/src/joystick/windows/SDL_rawinputjoystick.c b/src/joystick/windows/SDL_rawinputjoystick.c
index d66fe00..5765686 100644
--- a/src/joystick/windows/SDL_rawinputjoystick.c
+++ b/src/joystick/windows/SDL_rawinputjoystick.c
@@ -40,6 +40,7 @@
 #include "SDL_timer.h"
 #include "../usb_ids.h"
 #include "../SDL_sysjoystick.h"
+#include "../controller_type.h"
 #include "../../core/windows/SDL_windows.h"
 #include "../../core/windows/SDL_hid.h"
 #include "../hidapi/SDL_hidapijoystick_c.h"
@@ -102,6 +103,7 @@ typedef struct _SDL_RAWINPUT_Device
     Uint16 version;
     SDL_JoystickGUID guid;
     SDL_bool is_xinput;
+    SDL_bool is_xboxone;
     PHIDP_PREPARSED_DATA preparsed_data;
 
     HANDLE hDevice;
@@ -114,6 +116,7 @@ typedef struct _SDL_RAWINPUT_Device
 struct joystick_hwdata
 {
     SDL_bool is_xinput;
+    SDL_bool is_xboxone;
     PHIDP_PREPARSED_DATA preparsed_data;
     ULONG max_data_length;
     HIDP_DATA *data;
@@ -705,6 +708,7 @@ RAWINPUT_AddDevice(HANDLE hDevice)
     device->product_id = (Uint16)rdi.hid.dwProductId;
     device->version = (Uint16)rdi.hid.dwVersionNumber;
     device->is_xinput = SDL_TRUE;
+    device->is_xboxone = GuessControllerType(device->vendor_id, device->product_id) == k_eControllerType_XBoxOneController;
 
     {
         const Uint16 vendor = device->vendor_id;
@@ -1054,6 +1058,7 @@ RAWINPUT_JoystickOpen(SDL_Joystick *joystick, int device_index)
     }
 
     ctx->is_xinput = device->is_xinput;
+    ctx->is_xboxone = device->is_xboxone;
     ctx->preparsed_data = device->preparsed_data;
     ctx->max_data_length = SDL_HidP_MaxDataListLength(HidP_Input, ctx->preparsed_data);
     ctx->data = (HIDP_DATA *)SDL_malloc(ctx->max_data_length * sizeof(*ctx->data));
@@ -1280,6 +1285,25 @@ RAWINPUT_JoystickRumbleTriggers(SDL_Joystick *joystick, Uint16 left_rumble, Uint
 static Uint32
 RAWINPUT_JoystickGetCapabilities(SDL_Joystick *joystick)
 {
+    RAWINPUT_DeviceContext *ctx = joystick->hwdata;
+    Uint32 result = 0;
+
+#ifdef SDL_JOYSTICK_RAWINPUT_XINPUT
+    if (ctx->is_xinput) {
+        result |= SDL_JOYCAP_RUMBLE;
+    }
+#endif
+
+#ifdef SDL_JOYSTICK_RAWINPUT_WGI
+    if (ctx->is_xinput) {
+        result |= SDL_JOYCAP_RUMBLE;
+
+        if (ctx->is_xboxone) {
+            result |= SDL_JOYCAP_RUMBLE_TRIGGERS;
+        }
+    }
+#endif
+
     return 0;
 }
 
diff --git a/src/joystick/windows/SDL_windows_gaming_input.c b/src/joystick/windows/SDL_windows_gaming_input.c
index 475b6cd..33b4eb5 100644
--- a/src/joystick/windows/SDL_windows_gaming_input.c
+++ b/src/joystick/windows/SDL_windows_gaming_input.c
@@ -668,7 +668,14 @@ WGI_JoystickRumbleTriggers(SDL_Joystick *joystick, Uint16 left_rumble, Uint16 ri
 static Uint32
 WGI_JoystickGetCapabilities(SDL_Joystick *joystick)
 {
-    return 0;
+    struct joystick_hwdata *hwdata = joystick->hwdata;
+
+    if (hwdata->gamepad) {
+        /* FIXME: Can WGI even tell us if trigger rumble is supported? */
+        return SDL_JOYCAP_RUMBLE | SDL_JOYCAP_RUMBLE_TRIGGERS;
+    } else {
+        return 0;
+    }
 }
 
 static int
diff --git a/src/joystick/windows/SDL_xinputjoystick.c b/src/joystick/windows/SDL_xinputjoystick.c
index 350cace..b265e3f 100644
--- a/src/joystick/windows/SDL_xinputjoystick.c
+++ b/src/joystick/windows/SDL_xinputjoystick.c
@@ -506,7 +506,7 @@ SDL_XINPUT_JoystickRumble(SDL_Joystick * joystick, Uint16 low_frequency_rumble, 
 Uint32
 SDL_XINPUT_JoystickGetCapabilities(SDL_Joystick * joystick)
 {
-    return 0;
+    return SDL_JOYCAP_RUMBLE;
 }
 
 void