SDL: Added SDL_HINT_JOYSTICK_IOKIT and SDL_HINT_JOYSTICK_MFI to control whether the IOKit and GCController drivers should be used...

From 708f18d49ef6975769865d247e2ce4da6ce8da76 Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Tue, 14 Nov 2023 10:28:19 -0800
Subject: [PATCH] Added SDL_HINT_JOYSTICK_IOKIT and SDL_HINT_JOYSTICK_MFI to
 control whether the IOKit and GCController drivers should be used for
 joystick support.

This can be used to work around issues where the Apple GCController driver doesn't work for some controllers but there's no way to know which GCController maps to which IOKit device.
---
 include/SDL3/SDL_hints.h                | 18 ++++++++++++++++++
 src/joystick/apple/SDL_mfijoystick.m    |  8 ++++++++
 src/joystick/darwin/SDL_iokitjoystick.c | 14 ++++++++------
 3 files changed, 34 insertions(+), 6 deletions(-)

diff --git a/include/SDL3/SDL_hints.h b/include/SDL3/SDL_hints.h
index bac7e5086682..d2c2b0d8a73a 100644
--- a/include/SDL3/SDL_hints.h
+++ b/include/SDL3/SDL_hints.h
@@ -965,6 +965,24 @@ extern "C" {
  */
 #define SDL_HINT_JOYSTICK_HIDAPI_XBOX_ONE_HOME_LED "SDL_JOYSTICK_HIDAPI_XBOX_ONE_HOME_LED"
 
+/**
+  *  A variable controlling whether IOKit should be used for controller handling.
+  *
+  *  This variable can be set to the following values:
+  *    "0"       - IOKit is not used
+  *    "1"       - IOKit is used (the default)
+  */
+#define SDL_HINT_JOYSTICK_IOKIT "SDL_JOYSTICK_IOKIT"
+
+/**
+  *  A variable controlling whether GCController should be used for controller handling.
+  *
+  *  This variable can be set to the following values:
+  *    "0"       - GCController is not used
+  *    "1"       - GCController is used (the default)
+  */
+#define SDL_HINT_JOYSTICK_MFI "SDL_JOYSTICK_MFI"
+
 /**
   *  A variable controlling whether the RAWINPUT joystick drivers should be used for better handling XInput-capable devices.
   *
diff --git a/src/joystick/apple/SDL_mfijoystick.m b/src/joystick/apple/SDL_mfijoystick.m
index e53ddd01fa2b..ebe5c7bcadc8 100644
--- a/src/joystick/apple/SDL_mfijoystick.m
+++ b/src/joystick/apple/SDL_mfijoystick.m
@@ -694,6 +694,10 @@ static void SDLCALL SDL_AppleTVRemoteRotationHintChanged(void *udata, const char
 
 static int IOS_JoystickInit(void)
 {
+    if (!SDL_GetHintBoolean(SDL_HINT_JOYSTICK_MFI, SDL_TRUE)) {
+        return 0;
+    }
+
 #ifdef __MACOS__
 #if SDL_HAS_BUILTIN(__builtin_available)
     if (@available(macOS 10.16, *)) {
@@ -1741,6 +1745,10 @@ static SDL_bool IOS_JoystickGetGamepadMapping(int device_index, SDL_GamepadMappi
 #if defined(SDL_JOYSTICK_MFI) && defined(__MACOS__)
 SDL_bool IOS_SupportedHIDDevice(IOHIDDeviceRef device)
 {
+    if (!connectObserver) {
+        return SDL_FALSE;
+    }
+
     if (@available(macOS 10.16, *)) {
         const int MAX_ATTEMPTS = 3;
         for (int attempt = 0; attempt < MAX_ATTEMPTS; ++attempt) {
diff --git a/src/joystick/darwin/SDL_iokitjoystick.c b/src/joystick/darwin/SDL_iokitjoystick.c
index 288909504779..50cb2e107b3b 100644
--- a/src/joystick/darwin/SDL_iokitjoystick.c
+++ b/src/joystick/darwin/SDL_iokitjoystick.c
@@ -655,8 +655,8 @@ static SDL_bool CreateHIDManager(void)
 
 static int DARWIN_JoystickInit(void)
 {
-    if (gpDeviceList) {
-        return SDL_SetError("Joystick: Device list already inited.");
+    if (!SDL_GetHintBoolean(SDL_HINT_JOYSTICK_IOKIT, SDL_TRUE)) {
+        return 0;
     }
 
     if (!CreateHIDManager()) {
@@ -692,10 +692,12 @@ static void DARWIN_JoystickDetect(void)
         }
     }
 
-    /* run this after the checks above so we don't set device->removed and delete the device before
-       DARWIN_JoystickUpdate can run to clean up the SDL_Joystick object that owns this device */
-    while (CFRunLoopRunInMode(SDL_JOYSTICK_RUNLOOP_MODE, 0, TRUE) == kCFRunLoopRunHandledSource) {
-        /* no-op. Pending callbacks will fire in CFRunLoopRunInMode(). */
+    if (hidman) {
+        /* run this after the checks above so we don't set device->removed and delete the device before
+           DARWIN_JoystickUpdate can run to clean up the SDL_Joystick object that owns this device */
+        while (CFRunLoopRunInMode(SDL_JOYSTICK_RUNLOOP_MODE, 0, TRUE) == kCFRunLoopRunHandledSource) {
+            /* no-op. Pending callbacks will fire in CFRunLoopRunInMode(). */
+        }
     }
 }