Browse Source

Fixed opening one Joy-Con when the other is visible but disconnected

This can happen on Windows when the controller is turned off directly. It still shows up in the device list and you can send packets to it, but it's off and doesn't respond. We'll mark this device as broken and open the other as a single Joy-Con.
Sam Lantinga 1 month ago
parent
commit
34c3734953

+ 2 - 0
src/joystick/hidapi/SDL_hidapi_combined.c

@@ -70,6 +70,8 @@ static bool HIDAPI_DriverCombined_OpenJoystick(SDL_HIDAPI_Device *device, SDL_Jo
     for (i = 0; i < device->num_children; ++i) {
         SDL_HIDAPI_Device *child = device->children[i];
         if (!child->driver->OpenJoystick(child, joystick)) {
+            child->broken = true;
+
             while (i-- > 0) {
                 child = device->children[i];
                 child->driver->CloseJoystick(child, joystick);

+ 1 - 0
src/joystick/hidapi/SDL_hidapi_switch.c

@@ -2727,6 +2727,7 @@ static bool HIDAPI_DriverSwitch_UpdateDevice(SDL_HIDAPI_Device *device)
 
     // Reconnect the Bluetooth device once the USB device is gone
     if (device->num_joysticks == 0 && device->is_bluetooth && packet_count > 0 &&
+        !device->parent &&
         !HIDAPI_HasConnectedUSBDevice(device->serial)) {
         HIDAPI_JoystickConnected(device, NULL);
     }

+ 14 - 4
src/joystick/hidapi/SDL_hidapijoystick.c

@@ -363,7 +363,7 @@ static SDL_HIDAPI_Device *HIDAPI_GetDeviceByIndex(int device_index, SDL_Joystick
     SDL_AssertJoysticksLocked();
 
     for (device = SDL_HIDAPI_devices; device; device = device->next) {
-        if (device->parent) {
+        if (device->parent || device->broken) {
             continue;
         }
         if (device->driver) {
@@ -685,7 +685,7 @@ bool HIDAPI_HasConnectedUSBDevice(const char *serial)
     }
 
     for (device = SDL_HIDAPI_devices; device; device = device->next) {
-        if (!device->driver) {
+        if (!device->driver || device->broken) {
             continue;
         }
 
@@ -711,7 +711,7 @@ void HIDAPI_DisconnectBluetoothDevice(const char *serial)
     }
 
     for (device = SDL_HIDAPI_devices; device; device = device->next) {
-        if (!device->driver) {
+        if (!device->driver || device->broken) {
             continue;
         }
 
@@ -1016,6 +1016,10 @@ static bool HIDAPI_CreateCombinedJoyCons(void)
             // This device is already part of a combined device
             continue;
         }
+        if (device->broken) {
+            // This device can't be used
+            continue;
+        }
 
         SDL_GetJoystickGUIDInfo(device->guid, &vendor, &product, NULL, NULL);
 
@@ -1136,6 +1140,12 @@ check_removed:
                 SDL_HIDAPI_change_count = 0;
             }
         }
+        if (device->broken && device->parent) {
+            HIDAPI_DelDevice(device->parent);
+
+            // We deleted a different device here, restart the loop
+            goto check_removed;
+        }
         device = next;
     }
 
@@ -1478,7 +1488,7 @@ static bool HIDAPI_JoystickOpen(SDL_Joystick *joystick, int device_index)
 
     SDL_AssertJoysticksLocked();
 
-    if (!device || !device->driver) {
+    if (!device || !device->driver || device->broken) {
         // This should never happen - validated before being called
         return SDL_SetError("Couldn't find HIDAPI device at index %d", device_index);
     }

+ 4 - 0
src/joystick/hidapi/SDL_hidapijoystick_c.h

@@ -101,6 +101,10 @@ typedef struct SDL_HIDAPI_Device
     // Used to flag that the device is being updated
     bool updating;
 
+    // Used to flag devices that failed open
+    // This can happen on Windows with Bluetooth devices that have turned off
+    bool broken;
+
     struct SDL_HIDAPI_Device *parent;
     int num_children;
     struct SDL_HIDAPI_Device **children;