Commit 012471e959f3a791ddc878ee32744a9f4dd48cd0

Sam Lantinga 2020-11-27T18:57:38

Open and test all connected controllers

diff --git a/test/testgamecontroller.c b/test/testgamecontroller.c
index 52d8eec..57682bb 100644
--- a/test/testgamecontroller.c
+++ b/test/testgamecontroller.c
@@ -68,79 +68,168 @@ SDL_bool done = SDL_FALSE;
 SDL_bool set_LED = SDL_FALSE;
 SDL_Texture *background_front, *background_back, *button, *axis;
 SDL_GameController *gamecontroller;
+SDL_GameController **gamecontrollers;
+int num_controllers = 0;
 
-static SDL_Texture *
-LoadTexture(SDL_Renderer *renderer, const char *file, SDL_bool transparent)
+static void UpdateWindowTitle()
 {
-    SDL_Surface *temp = NULL;
-    SDL_Texture *texture = NULL;
+    if (!window) {
+        return;
+    }
 
-    temp = SDL_LoadBMP(file);
-    if (temp == NULL) {
-        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't load %s: %s", file, SDL_GetError());
-    } else {
-        /* Set transparent pixel as the pixel at (0,0) */
-        if (transparent) {
-            if (temp->format->BytesPerPixel == 1) {
-                SDL_SetColorKey(temp, SDL_TRUE, *(Uint8 *)temp->pixels);
+    if (gamecontroller) {
+        const char *name = SDL_GameControllerName(gamecontroller);
+        const char *serial = SDL_GameControllerGetSerial(gamecontroller);
+        const char *basetitle = "Game Controller Test: ";
+        const size_t titlelen = SDL_strlen(basetitle) + SDL_strlen(name) + (serial ? 3 + SDL_strlen(serial) : 0) + 1;
+        char *title = (char *)SDL_malloc(titlelen);
+
+        retval = SDL_FALSE;
+        done = SDL_FALSE;
+
+        if (title) {
+            SDL_snprintf(title, titlelen, "%s%s", basetitle, name);
+            if (serial) {
+                SDL_strlcat(title, " (", titlelen);
+                SDL_strlcat(title, serial, titlelen);
+                SDL_strlcat(title, ")", titlelen);
             }
+            SDL_SetWindowTitle(window, title);
+            SDL_free(title);
         }
+    } else {
+        SDL_SetWindowTitle(window, "Waiting for controller...");
+    }
+}
 
-        texture = SDL_CreateTextureFromSurface(renderer, temp);
-        if (!texture) {
-            SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create texture: %s\n", SDL_GetError());
+static int FindController(SDL_JoystickID controller_id)
+{
+    int i;
+
+    for (i = 0; i < num_controllers; ++i) {
+        if (controller_id == SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(gamecontrollers[i]))) {
+            return i;
         }
     }
-    if (temp) {
-        SDL_FreeSurface(temp);
-    }
-    return texture;
+    return -1;
 }
 
-static void
-InitGameController()
+static void AddController(int device_index, SDL_bool verbose)
 {
-    const char *name = SDL_GameControllerName(gamecontroller);
+    SDL_JoystickID controller_id = SDL_JoystickGetDeviceInstanceID(device_index);
+    SDL_GameController *controller;
+    SDL_GameController **controllers;
+
+    controller_id = SDL_JoystickGetDeviceInstanceID(device_index);
+    if (controller_id < 0) {
+        SDL_Log("Couldn't get controller ID: %s\n", SDL_GetError());
+        return;
+    }
 
-    SDL_Log("Opened game controller %s\n", name);
+    if (FindController(controller_id) >= 0) {
+        /* We already have this controller */
+        return;
+    }
+
+    controller = SDL_GameControllerOpen(device_index);
+    if (!controller) {
+        SDL_Log("Couldn't open controller: %s\n", SDL_GetError());
+        return;
+    }
+
+    controllers = (SDL_GameController **)SDL_realloc(gamecontrollers, (num_controllers + 1) * sizeof(*controllers));
+    if (!controllers) {
+        SDL_GameControllerClose(controller);
+        return;
+    }
+
+    controllers[num_controllers++] = controller;
+    gamecontrollers = controllers;
+    gamecontroller = controller;
+
+    if (verbose) {
+        const char *name = SDL_GameControllerName(gamecontroller);
+        SDL_Log("Opened game controller %s\n", name);
+    }
 
     if (SDL_GameControllerHasSensor(gamecontroller, SDL_SENSOR_ACCEL)) {
-        SDL_Log("Enabling accelerometer\n");
+        if (verbose) {
+            SDL_Log("Enabling accelerometer\n");
+        }
         SDL_GameControllerSetSensorEnabled(gamecontroller, SDL_SENSOR_ACCEL, SDL_TRUE);
     }
 
     if (SDL_GameControllerHasSensor(gamecontroller, SDL_SENSOR_GYRO)) {
-        SDL_Log("Enabling gyro\n");
+        if (verbose) {
+            SDL_Log("Enabling gyro\n");
+        }
         SDL_GameControllerSetSensorEnabled(gamecontroller, SDL_SENSOR_GYRO, SDL_TRUE);
     }
+
+    UpdateWindowTitle();
 }
 
-static void
-UpdateWindowTitle()
+static void SetController(SDL_JoystickID controller)
 {
-    if (gamecontroller) {
-        const char *name = SDL_GameControllerName(gamecontroller);
-        const char *serial = SDL_GameControllerGetSerial(gamecontroller);
-        const char *basetitle = "Game Controller Test: ";
-        const size_t titlelen = SDL_strlen(basetitle) + SDL_strlen(name) + (serial ? 3 + SDL_strlen(serial) : 0) + 1;
-        char *title = (char *)SDL_malloc(titlelen);
+    int i = FindController(controller);
 
-        retval = SDL_FALSE;
-        done = SDL_FALSE;
+    if (i < 0) {
+        return;
+    }
 
-        if (title) {
-            SDL_snprintf(title, titlelen, "%s%s", basetitle, name);
-            if (serial) {
-                SDL_strlcat(title, " (", titlelen);
-                SDL_strlcat(title, serial, titlelen);
-                SDL_strlcat(title, ")", titlelen);
+    if (gamecontroller != gamecontrollers[i]) {
+        gamecontroller = gamecontrollers[i];
+        UpdateWindowTitle();
+    }
+}
+
+static void DelController(SDL_JoystickID controller)
+{
+    int i = FindController(controller);
+
+    if (i < 0) {
+        return;
+    }
+
+    --num_controllers;
+    if (i < num_controllers) {
+        SDL_memcpy(&gamecontrollers[i], &gamecontrollers[i+1], (num_controllers - i) * sizeof(*gamecontrollers));
+    }
+
+    if (num_controllers > 0) {
+        gamecontroller = gamecontrollers[0];
+    } else {
+        gamecontroller = NULL;
+    }
+    UpdateWindowTitle();
+}
+
+static SDL_Texture *
+LoadTexture(SDL_Renderer *renderer, const char *file, SDL_bool transparent)
+{
+    SDL_Surface *temp = NULL;
+    SDL_Texture *texture = NULL;
+
+    temp = SDL_LoadBMP(file);
+    if (temp == NULL) {
+        SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't load %s: %s", file, SDL_GetError());
+    } else {
+        /* Set transparent pixel as the pixel at (0,0) */
+        if (transparent) {
+            if (temp->format->BytesPerPixel == 1) {
+                SDL_SetColorKey(temp, SDL_TRUE, *(Uint8 *)temp->pixels);
             }
-            SDL_SetWindowTitle(window, title);
-            SDL_free(title);
         }
-    } else {
-        SDL_SetWindowTitle(window, "Waiting for controller...");
+
+        texture = SDL_CreateTextureFromSurface(renderer, temp);
+        if (!texture) {
+            SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create texture: %s\n", SDL_GetError());
+        }
+    }
+    if (temp) {
+        SDL_FreeSurface(temp);
     }
+    return texture;
 }
 
 static Uint16 ConvertAxisToRumble(Sint16 axis)
@@ -164,30 +253,13 @@ loop(void *arg)
     while (SDL_PollEvent(&event)) {
         switch (event.type) {
         case SDL_CONTROLLERDEVICEADDED:
-            SDL_Log("Game controller device %d added.\n", (int) event.cdevice.which);
-            if (gamecontroller) {
-                SDL_GameControllerClose(gamecontroller);
-            }
-
-            gamecontroller = SDL_GameControllerOpen(event.cdevice.which);
-            if (gamecontroller) {
-                InitGameController();
-            } else {
-                SDL_Log("Couldn't open controller: %s\n", SDL_GetError());
-            }
-            UpdateWindowTitle();
+            SDL_Log("Game controller device %d added.\n", (int) SDL_JoystickGetDeviceInstanceID(event.cdevice.which));
+            AddController(event.cdevice.which, SDL_TRUE);
             break;
 
         case SDL_CONTROLLERDEVICEREMOVED:
             SDL_Log("Game controller device %d removed.\n", (int) event.cdevice.which);
-            if (gamecontroller && event.cdevice.which == SDL_JoystickInstanceID(SDL_GameControllerGetJoystick(gamecontroller))) {
-                SDL_GameControllerClose(gamecontroller);
-                gamecontroller = SDL_GameControllerOpen(0);
-                if (gamecontroller) {
-                    InitGameController();
-                }
-                UpdateWindowTitle();
-            }
+            DelController(event.cdevice.which);
             break;
 
         case SDL_CONTROLLERTOUCHPADDOWN:
@@ -214,11 +286,17 @@ loop(void *arg)
             break;
 
         case SDL_CONTROLLERAXISMOTION:
+            if (event.caxis.value <= (-SDL_JOYSTICK_AXIS_MAX / 2) || event.caxis.value >= (SDL_JOYSTICK_AXIS_MAX / 2)) {
+                SetController(event.caxis.which);
+            }
             SDL_Log("Controller axis %s changed to %d\n", SDL_GameControllerGetStringForAxis((SDL_GameControllerAxis)event.caxis.axis), event.caxis.value);
             break;
 
         case SDL_CONTROLLERBUTTONDOWN:
         case SDL_CONTROLLERBUTTONUP:
+            if (event.type == SDL_CONTROLLERBUTTONDOWN) {
+                SetController(event.cbutton.which);
+            }
             SDL_Log("Controller button %s %s\n", SDL_GameControllerGetStringForButton((SDL_GameControllerButton)event.cbutton.button), event.cbutton.state ? "pressed" : "released");
             break;
 
@@ -339,7 +417,8 @@ int
 main(int argc, char *argv[])
 {
     int i;
-    int nController = 0;
+    int controller_count = 0;
+    int controller_index = 0;
     char guid[64];
 
     SDL_SetHint(SDL_HINT_ACCELEROMETER_AS_JOYSTICK, "0");
@@ -377,8 +456,8 @@ main(int argc, char *argv[])
         SDL_JoystickGetGUIDString(SDL_JoystickGetDeviceGUID(i),
                                   guid, sizeof (guid));
 
-        if ( SDL_IsGameController(i) ) {
-            nController++;
+        if (SDL_IsGameController(i)) {
+            controller_count++;
             name = SDL_GameControllerNameForIndex(i);
             switch (SDL_GameControllerTypeForIndex(i)) {
             case SDL_CONTROLLER_TYPE_XBOX360:
@@ -403,6 +482,7 @@ main(int argc, char *argv[])
                 description = "Game Controller";
                 break;
             }
+            AddController(i, SDL_FALSE);
         } else {
             name = SDL_JoystickNameForIndex(i);
             description = "Joystick";
@@ -411,7 +491,7 @@ main(int argc, char *argv[])
             description, i, name ? name : "Unknown", guid,
             SDL_JoystickGetDeviceVendor(i), SDL_JoystickGetDeviceProduct(i), SDL_JoystickGetDevicePlayerIndex(i));
     }
-    SDL_Log("There are %d game controller(s) attached (%d joystick(s))\n", nController, SDL_NumJoysticks());
+    SDL_Log("There are %d game controller(s) attached (%d joystick(s))\n", controller_count, SDL_NumJoysticks());
 
     /* Create a window to display controller state */
     window = SDL_CreateWindow("Game Controller Test", SDL_WINDOWPOS_CENTERED,
@@ -453,9 +533,13 @@ main(int argc, char *argv[])
     /*SDL_RenderSetLogicalSize(screen, background->w, background->h);*/
 
     if (argv[1] && *argv[1] != '-') {
-        gamecontroller = SDL_GameControllerOpen(SDL_atoi(argv[1]));
+        controller_index = SDL_atoi(argv[1]);
+    }
+    if (controller_index < num_controllers) {
+        gamecontroller = gamecontrollers[controller_index];
+    } else {
+        gamecontroller = NULL;
     }
-
     UpdateWindowTitle();
 
     /* Loop, getting controller events! */