Explorar o código

testcontroller: use SDL_MAIN_USE_CALLBACKS so updates happen during live resizing

Sam Lantinga hai 2 meses
pai
achega
362f96a6cf
Modificáronse 1 ficheiros con 337 adicións e 344 borrados
  1. 337 344
      test/testcontroller.c

+ 337 - 344
test/testcontroller.c

@@ -12,6 +12,7 @@
 
 /* Simple program to test the SDL controller routines */
 
+#define SDL_MAIN_USE_CALLBACKS
 #include <SDL3/SDL.h>
 #include <SDL3/SDL_main.h>
 #include <SDL3/SDL_test.h>
@@ -64,6 +65,7 @@ typedef struct
     int trigger_effect;
 } Controller;
 
+static SDLTest_CommonState *state;
 static SDL_Window *window = NULL;
 static SDL_Renderer *screen = NULL;
 static ControllerDisplayMode display_mode = CONTROLLER_MODE_TESTING;
@@ -194,47 +196,47 @@ typedef struct
 
 static void CyclePS5AudioRoute(Controller *device)
 {
-    DS5EffectsState_t state;
+    DS5EffectsState_t effects;
 
     device->audio_route = (device->audio_route + 1) % 4;
 
-    SDL_zero(state);
+    SDL_zero(effects);
     switch (device->audio_route) {
     case 0:
         /* Audio disabled */
-        state.ucEnableBits1 |= (0x80 | 0x20 | 0x10); /* Modify audio route and speaker / headphone volume */
-        state.ucSpeakerVolume = 0;                   /* Minimum volume */
-        state.ucHeadphoneVolume = 0;                 /* Minimum volume */
-        state.ucAudioEnableBits = 0x00;              /* Output to headphones */
+        effects.ucEnableBits1 |= (0x80 | 0x20 | 0x10); /* Modify audio route and speaker / headphone volume */
+        effects.ucSpeakerVolume = 0;                   /* Minimum volume */
+        effects.ucHeadphoneVolume = 0;                 /* Minimum volume */
+        effects.ucAudioEnableBits = 0x00;              /* Output to headphones */
         break;
     case 1:
         /* Headphones */
-        state.ucEnableBits1 |= (0x80 | 0x10); /* Modify audio route and headphone volume */
-        state.ucHeadphoneVolume = 50;         /* 50% volume - don't blast into the ears */
-        state.ucAudioEnableBits = 0x00;       /* Output to headphones */
+        effects.ucEnableBits1 |= (0x80 | 0x10); /* Modify audio route and headphone volume */
+        effects.ucHeadphoneVolume = 50;         /* 50% volume - don't blast into the ears */
+        effects.ucAudioEnableBits = 0x00;       /* Output to headphones */
         break;
     case 2:
         /* Speaker */
-        state.ucEnableBits1 |= (0x80 | 0x20); /* Modify audio route and speaker volume */
-        state.ucSpeakerVolume = 100;          /* Maximum volume */
-        state.ucAudioEnableBits = 0x30;       /* Output to speaker */
+        effects.ucEnableBits1 |= (0x80 | 0x20); /* Modify audio route and speaker volume */
+        effects.ucSpeakerVolume = 100;          /* Maximum volume */
+        effects.ucAudioEnableBits = 0x30;       /* Output to speaker */
         break;
     case 3:
         /* Both */
-        state.ucEnableBits1 |= (0x80 | 0x20 | 0x10); /* Modify audio route and speaker / headphone volume */
-        state.ucSpeakerVolume = 100;                 /* Maximum volume */
-        state.ucHeadphoneVolume = 50;                /* 50% volume - don't blast into the ears */
-        state.ucAudioEnableBits = 0x20;              /* Output to both speaker and headphones */
+        effects.ucEnableBits1 |= (0x80 | 0x20 | 0x10); /* Modify audio route and speaker / headphone volume */
+        effects.ucSpeakerVolume = 100;                 /* Maximum volume */
+        effects.ucHeadphoneVolume = 50;                /* 50% volume - don't blast into the ears */
+        effects.ucAudioEnableBits = 0x20;              /* Output to both speaker and headphones */
         break;
     }
-    SDL_SendGamepadEffect(device->gamepad, &state, sizeof(state));
+    SDL_SendGamepadEffect(device->gamepad, &effects, sizeof(effects));
 }
 
 static void CyclePS5TriggerEffect(Controller *device)
 {
-    DS5EffectsState_t state;
+    DS5EffectsState_t effects;
 
-    Uint8 effects[3][11] = {
+    Uint8 trigger_effects[3][11] = {
         /* Clear trigger effect */
         { 0x05, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
         /* Constant resistance across entire trigger pull */
@@ -243,13 +245,13 @@ static void CyclePS5TriggerEffect(Controller *device)
         { 0x06, 15, 63, 128, 0, 0, 0, 0, 0, 0, 0 },
     };
 
-    device->trigger_effect = (device->trigger_effect + 1) % SDL_arraysize(effects);
+    device->trigger_effect = (device->trigger_effect + 1) % SDL_arraysize(trigger_effects);
 
-    SDL_zero(state);
-    state.ucEnableBits1 |= (0x04 | 0x08); /* Modify right and left trigger effect respectively */
-    SDL_memcpy(state.rgucRightTriggerEffect, effects[device->trigger_effect], sizeof(effects[0]));
-    SDL_memcpy(state.rgucLeftTriggerEffect, effects[device->trigger_effect], sizeof(effects[0]));
-    SDL_SendGamepadEffect(device->gamepad, &state, sizeof(state));
+    SDL_zero(effects);
+    effects.ucEnableBits1 |= (0x04 | 0x08); /* Modify right and left trigger effect respectively */
+    SDL_memcpy(effects.rgucRightTriggerEffect, trigger_effects[device->trigger_effect], sizeof(trigger_effects[0]));
+    SDL_memcpy(effects.rgucLeftTriggerEffect, trigger_effects[device->trigger_effect], sizeof(trigger_effects[0]));
+    SDL_SendGamepadEffect(device->gamepad, &effects, sizeof(effects));
 }
 
 static void ClearButtonHighlights(void)
@@ -1573,358 +1575,359 @@ static void UpdateGamepadEffects(void)
     }
 }
 
-static void loop(void *arg)
+SDL_AppResult SDLCALL SDL_AppEvent(void *appstate, SDL_Event *event)
 {
-    SDL_Event event;
+    SDL_ConvertEventToRenderCoordinates(screen, event);
 
-    /* If we have a virtual controller, send a virtual accelerometer sensor reading */
-    if (virtual_joystick) {
-        float data[3] = { 0.0f, SDL_STANDARD_GRAVITY, 0.0f };
-        SDL_SendJoystickVirtualSensorData(virtual_joystick, SDL_SENSOR_ACCEL, SDL_GetTicksNS(), data, SDL_arraysize(data));
-    }
-
-    /* Update to get the current event state */
-    SDL_PumpEvents();
-
-    /* Process all currently pending events */
-    while (SDL_PeepEvents(&event, 1, SDL_GETEVENT, SDL_EVENT_FIRST, SDL_EVENT_LAST) == 1) {
-        SDL_ConvertEventToRenderCoordinates(screen, &event);
-
-        switch (event.type) {
-        case SDL_EVENT_JOYSTICK_ADDED:
-            AddController(event.jdevice.which, true);
-            break;
+    switch (event->type) {
+    case SDL_EVENT_JOYSTICK_ADDED:
+        AddController(event->jdevice.which, true);
+        break;
 
-        case SDL_EVENT_JOYSTICK_REMOVED:
-            DelController(event.jdevice.which);
-            break;
+    case SDL_EVENT_JOYSTICK_REMOVED:
+        DelController(event->jdevice.which);
+        break;
 
-        case SDL_EVENT_JOYSTICK_AXIS_MOTION:
-            if (display_mode == CONTROLLER_MODE_TESTING) {
-                if (event.jaxis.value <= (-SDL_JOYSTICK_AXIS_MAX / 2) || event.jaxis.value >= (SDL_JOYSTICK_AXIS_MAX / 2)) {
-                    SetController(event.jaxis.which);
-                }
-            } else if (display_mode == CONTROLLER_MODE_BINDING &&
-                       event.jaxis.which == controller->id &&
-                       event.jaxis.axis < controller->num_axes &&
-                       binding_element != SDL_GAMEPAD_ELEMENT_INVALID) {
-                const int MAX_ALLOWED_JITTER = SDL_JOYSTICK_AXIS_MAX / 80; /* ShanWan PS3 gamepad needed 96 */
-                AxisState *pAxisState = &controller->axis_state[event.jaxis.axis];
-                int nValue = event.jaxis.value;
-                int nCurrentDistance, nFarthestDistance;
-                if (!pAxisState->m_bMoving) {
-                    Sint16 nInitialValue;
-                    pAxisState->m_bMoving = SDL_GetJoystickAxisInitialState(controller->joystick, event.jaxis.axis, &nInitialValue);
-                    pAxisState->m_nLastValue = nValue;
-                    pAxisState->m_nStartingValue = nInitialValue;
-                    pAxisState->m_nFarthestValue = nInitialValue;
-                } else if (SDL_abs(nValue - pAxisState->m_nLastValue) <= MAX_ALLOWED_JITTER) {
-                    break;
-                } else {
-                    pAxisState->m_nLastValue = nValue;
-                }
-                nCurrentDistance = SDL_abs(nValue - pAxisState->m_nStartingValue);
+    case SDL_EVENT_JOYSTICK_AXIS_MOTION:
+        if (display_mode == CONTROLLER_MODE_TESTING) {
+            if (event->jaxis.value <= (-SDL_JOYSTICK_AXIS_MAX / 2) || event->jaxis.value >= (SDL_JOYSTICK_AXIS_MAX / 2)) {
+                SetController(event->jaxis.which);
+            }
+        } else if (display_mode == CONTROLLER_MODE_BINDING &&
+                   event->jaxis.which == controller->id &&
+                   event->jaxis.axis < controller->num_axes &&
+                   binding_element != SDL_GAMEPAD_ELEMENT_INVALID) {
+            const int MAX_ALLOWED_JITTER = SDL_JOYSTICK_AXIS_MAX / 80; /* ShanWan PS3 gamepad needed 96 */
+            AxisState *pAxisState = &controller->axis_state[event->jaxis.axis];
+            int nValue = event->jaxis.value;
+            int nCurrentDistance, nFarthestDistance;
+            if (!pAxisState->m_bMoving) {
+                Sint16 nInitialValue;
+                pAxisState->m_bMoving = SDL_GetJoystickAxisInitialState(controller->joystick, event->jaxis.axis, &nInitialValue);
+                pAxisState->m_nLastValue = nValue;
+                pAxisState->m_nStartingValue = nInitialValue;
+                pAxisState->m_nFarthestValue = nInitialValue;
+            } else if (SDL_abs(nValue - pAxisState->m_nLastValue) <= MAX_ALLOWED_JITTER) {
+                break;
+            } else {
+                pAxisState->m_nLastValue = nValue;
+            }
+            nCurrentDistance = SDL_abs(nValue - pAxisState->m_nStartingValue);
+            nFarthestDistance = SDL_abs(pAxisState->m_nFarthestValue - pAxisState->m_nStartingValue);
+            if (nCurrentDistance > nFarthestDistance) {
+                pAxisState->m_nFarthestValue = nValue;
                 nFarthestDistance = SDL_abs(pAxisState->m_nFarthestValue - pAxisState->m_nStartingValue);
-                if (nCurrentDistance > nFarthestDistance) {
-                    pAxisState->m_nFarthestValue = nValue;
-                    nFarthestDistance = SDL_abs(pAxisState->m_nFarthestValue - pAxisState->m_nStartingValue);
-                }
+            }
 
 #ifdef DEBUG_AXIS_MAPPING
-                SDL_Log("AXIS %d nValue %d nCurrentDistance %d nFarthestDistance %d\n", event.jaxis.axis, nValue, nCurrentDistance, nFarthestDistance);
+            SDL_Log("AXIS %d nValue %d nCurrentDistance %d nFarthestDistance %d\n", event->jaxis.axis, nValue, nCurrentDistance, nFarthestDistance);
 #endif
-                /* If we've gone out far enough and started to come back, let's bind this axis */
-                if (nFarthestDistance >= 16000 && nCurrentDistance <= 10000) {
-                    char binding[12];
-                    int axis_min = StandardizeAxisValue(pAxisState->m_nStartingValue);
-                    int axis_max = StandardizeAxisValue(pAxisState->m_nFarthestValue);
-
-                    if (axis_min == 0 && axis_max == SDL_JOYSTICK_AXIS_MIN) {
-                        /* The negative half axis */
-                        (void)SDL_snprintf(binding, sizeof(binding), "-a%d", event.jaxis.axis);
-                    } else if (axis_min == 0 && axis_max == SDL_JOYSTICK_AXIS_MAX) {
-                        /* The positive half axis */
-                        (void)SDL_snprintf(binding, sizeof(binding), "+a%d", event.jaxis.axis);
-                    } else {
-                        (void)SDL_snprintf(binding, sizeof(binding), "a%d", event.jaxis.axis);
-                        if (axis_min > axis_max) {
-                            /* Invert the axis */
-                            SDL_strlcat(binding, "~", SDL_arraysize(binding));
-                        }
+            /* If we've gone out far enough and started to come back, let's bind this axis */
+            if (nFarthestDistance >= 16000 && nCurrentDistance <= 10000) {
+                char binding[12];
+                int axis_min = StandardizeAxisValue(pAxisState->m_nStartingValue);
+                int axis_max = StandardizeAxisValue(pAxisState->m_nFarthestValue);
+
+                if (axis_min == 0 && axis_max == SDL_JOYSTICK_AXIS_MIN) {
+                    /* The negative half axis */
+                    (void)SDL_snprintf(binding, sizeof(binding), "-a%d", event->jaxis.axis);
+                } else if (axis_min == 0 && axis_max == SDL_JOYSTICK_AXIS_MAX) {
+                    /* The positive half axis */
+                    (void)SDL_snprintf(binding, sizeof(binding), "+a%d", event->jaxis.axis);
+                } else {
+                    (void)SDL_snprintf(binding, sizeof(binding), "a%d", event->jaxis.axis);
+                    if (axis_min > axis_max) {
+                        /* Invert the axis */
+                        SDL_strlcat(binding, "~", SDL_arraysize(binding));
                     }
+                }
 #ifdef DEBUG_AXIS_MAPPING
-                    SDL_Log("AXIS %d axis_min = %d, axis_max = %d, binding = %s\n", event.jaxis.axis, axis_min, axis_max, binding);
+                SDL_Log("AXIS %d axis_min = %d, axis_max = %d, binding = %s\n", event->jaxis.axis, axis_min, axis_max, binding);
 #endif
-                    CommitBindingElement(binding, false);
-                }
+                CommitBindingElement(binding, false);
             }
-            break;
+        }
+        break;
 
-        case SDL_EVENT_JOYSTICK_BUTTON_DOWN:
-            if (display_mode == CONTROLLER_MODE_TESTING) {
-                SetController(event.jbutton.which);
-            }
-            break;
+    case SDL_EVENT_JOYSTICK_BUTTON_DOWN:
+        if (display_mode == CONTROLLER_MODE_TESTING) {
+            SetController(event->jbutton.which);
+        }
+        break;
 
-        case SDL_EVENT_JOYSTICK_BUTTON_UP:
-            if (display_mode == CONTROLLER_MODE_BINDING &&
-                event.jbutton.which == controller->id &&
-                binding_element != SDL_GAMEPAD_ELEMENT_INVALID) {
-                char binding[12];
+    case SDL_EVENT_JOYSTICK_BUTTON_UP:
+        if (display_mode == CONTROLLER_MODE_BINDING &&
+            event->jbutton.which == controller->id &&
+            binding_element != SDL_GAMEPAD_ELEMENT_INVALID) {
+            char binding[12];
 
-                SDL_snprintf(binding, sizeof(binding), "b%d", event.jbutton.button);
-                CommitBindingElement(binding, false);
-            }
-            break;
+            SDL_snprintf(binding, sizeof(binding), "b%d", event->jbutton.button);
+            CommitBindingElement(binding, false);
+        }
+        break;
 
-        case SDL_EVENT_JOYSTICK_HAT_MOTION:
-            if (display_mode == CONTROLLER_MODE_BINDING &&
-                event.jhat.which == controller->id &&
-                event.jhat.value != SDL_HAT_CENTERED &&
-                binding_element != SDL_GAMEPAD_ELEMENT_INVALID) {
-                char binding[12];
+    case SDL_EVENT_JOYSTICK_HAT_MOTION:
+        if (display_mode == CONTROLLER_MODE_BINDING &&
+            event->jhat.which == controller->id &&
+            event->jhat.value != SDL_HAT_CENTERED &&
+            binding_element != SDL_GAMEPAD_ELEMENT_INVALID) {
+            char binding[12];
 
-                SDL_snprintf(binding, sizeof(binding), "h%d.%d", event.jhat.hat, event.jhat.value);
-                CommitBindingElement(binding, false);
-            }
-            break;
+            SDL_snprintf(binding, sizeof(binding), "h%d.%d", event->jhat.hat, event->jhat.value);
+            CommitBindingElement(binding, false);
+        }
+        break;
 
-        case SDL_EVENT_GAMEPAD_ADDED:
-            HandleGamepadAdded(event.gdevice.which, true);
-            break;
+    case SDL_EVENT_GAMEPAD_ADDED:
+        HandleGamepadAdded(event->gdevice.which, true);
+        break;
 
-        case SDL_EVENT_GAMEPAD_REMOVED:
-            HandleGamepadRemoved(event.gdevice.which);
-            break;
+    case SDL_EVENT_GAMEPAD_REMOVED:
+        HandleGamepadRemoved(event->gdevice.which);
+        break;
 
-        case SDL_EVENT_GAMEPAD_REMAPPED:
-            HandleGamepadRemapped(event.gdevice.which);
-            break;
+    case SDL_EVENT_GAMEPAD_REMAPPED:
+        HandleGamepadRemapped(event->gdevice.which);
+        break;
 
-        case SDL_EVENT_GAMEPAD_STEAM_HANDLE_UPDATED:
-            RefreshControllerName();
-            break;
+    case SDL_EVENT_GAMEPAD_STEAM_HANDLE_UPDATED:
+        RefreshControllerName();
+        break;
 
 #ifdef VERBOSE_TOUCHPAD
-        case SDL_EVENT_GAMEPAD_TOUCHPAD_DOWN:
-        case SDL_EVENT_GAMEPAD_TOUCHPAD_MOTION:
-        case SDL_EVENT_GAMEPAD_TOUCHPAD_UP:
-            SDL_Log("Gamepad %" SDL_PRIu32 " touchpad %" SDL_PRIs32 " finger %" SDL_PRIs32 " %s %.2f, %.2f, %.2f\n",
-                    event.gtouchpad.which,
-                    event.gtouchpad.touchpad,
-                    event.gtouchpad.finger,
-                    (event.type == SDL_EVENT_GAMEPAD_TOUCHPAD_DOWN ? "pressed at" : (event.type == SDL_EVENT_GAMEPAD_TOUCHPAD_UP ? "released at" : "moved to")),
-                    event.gtouchpad.x,
-                    event.gtouchpad.y,
-                    event.gtouchpad.pressure);
-            break;
+    case SDL_EVENT_GAMEPAD_TOUCHPAD_DOWN:
+    case SDL_EVENT_GAMEPAD_TOUCHPAD_MOTION:
+    case SDL_EVENT_GAMEPAD_TOUCHPAD_UP:
+        SDL_Log("Gamepad %" SDL_PRIu32 " touchpad %" SDL_PRIs32 " finger %" SDL_PRIs32 " %s %.2f, %.2f, %.2f\n",
+                event->gtouchpad.which,
+                event->gtouchpad.touchpad,
+                event->gtouchpad.finger,
+                (event->type == SDL_EVENT_GAMEPAD_TOUCHPAD_DOWN ? "pressed at" : (event->type == SDL_EVENT_GAMEPAD_TOUCHPAD_UP ? "released at" : "moved to")),
+                event->gtouchpad.x,
+                event->gtouchpad.y,
+                event->gtouchpad.pressure);
+        break;
 #endif /* VERBOSE_TOUCHPAD */
 
 #ifdef VERBOSE_SENSORS
-        case SDL_EVENT_GAMEPAD_SENSOR_UPDATE:
-            SDL_Log("Gamepad %" SDL_PRIu32 " sensor %s: %.2f, %.2f, %.2f (%" SDL_PRIu64 ")\n",
-                    event.gsensor.which,
-                    GetSensorName((SDL_SensorType) event.gsensor.sensor),
-                    event.gsensor.data[0],
-                    event.gsensor.data[1],
-                    event.gsensor.data[2],
-                    event.gsensor.sensor_timestamp);
-            break;
+    case SDL_EVENT_GAMEPAD_SENSOR_UPDATE:
+        SDL_Log("Gamepad %" SDL_PRIu32 " sensor %s: %.2f, %.2f, %.2f (%" SDL_PRIu64 ")\n",
+                event->gsensor.which,
+                GetSensorName((SDL_SensorType) event->gsensor.sensor),
+                event->gsensor.data[0],
+                event->gsensor.data[1],
+                event->gsensor.data[2],
+                event->gsensor.sensor_timestamp);
+        break;
 #endif /* VERBOSE_SENSORS */
 
 #ifdef VERBOSE_AXES
-        case SDL_EVENT_GAMEPAD_AXIS_MOTION:
-            if (display_mode == CONTROLLER_MODE_TESTING) {
-                if (event.gaxis.value <= (-SDL_JOYSTICK_AXIS_MAX / 2) || event.gaxis.value >= (SDL_JOYSTICK_AXIS_MAX / 2)) {
-                    SetController(event.gaxis.which);
-                }
+    case SDL_EVENT_GAMEPAD_AXIS_MOTION:
+        if (display_mode == CONTROLLER_MODE_TESTING) {
+            if (event->gaxis.value <= (-SDL_JOYSTICK_AXIS_MAX / 2) || event->gaxis.value >= (SDL_JOYSTICK_AXIS_MAX / 2)) {
+                SetController(event->gaxis.which);
             }
-            SDL_Log("Gamepad %" SDL_PRIu32 " axis %s changed to %d\n",
-                    event.gaxis.which,
-                    SDL_GetGamepadStringForAxis((SDL_GamepadAxis) event.gaxis.axis),
-                    event.gaxis.value);
-            break;
+        }
+        SDL_Log("Gamepad %" SDL_PRIu32 " axis %s changed to %d\n",
+                event->gaxis.which,
+                SDL_GetGamepadStringForAxis((SDL_GamepadAxis) event->gaxis.axis),
+                event->gaxis.value);
+        break;
 #endif /* VERBOSE_AXES */
 
-        case SDL_EVENT_GAMEPAD_BUTTON_DOWN:
-        case SDL_EVENT_GAMEPAD_BUTTON_UP:
-            if (display_mode == CONTROLLER_MODE_TESTING) {
-                if (event.type == SDL_EVENT_GAMEPAD_BUTTON_DOWN) {
-                    SetController(event.gbutton.which);
-                }
+    case SDL_EVENT_GAMEPAD_BUTTON_DOWN:
+    case SDL_EVENT_GAMEPAD_BUTTON_UP:
+        if (display_mode == CONTROLLER_MODE_TESTING) {
+            if (event->type == SDL_EVENT_GAMEPAD_BUTTON_DOWN) {
+                SetController(event->gbutton.which);
             }
+        }
 #ifdef VERBOSE_BUTTONS
-            SDL_Log("Gamepad %" SDL_PRIu32 " button %s %s\n",
-                    event.gbutton.which,
-                    SDL_GetGamepadStringForButton((SDL_GamepadButton) event.gbutton.button),
-                    event.gbutton.state ? "pressed" : "released");
+        SDL_Log("Gamepad %" SDL_PRIu32 " button %s %s\n",
+                event->gbutton.which,
+                SDL_GetGamepadStringForButton((SDL_GamepadButton) event->gbutton.button),
+                event->gbutton.state ? "pressed" : "released");
 #endif /* VERBOSE_BUTTONS */
 
-            if (display_mode == CONTROLLER_MODE_TESTING) {
-                if (event.type == SDL_EVENT_GAMEPAD_BUTTON_DOWN &&
-                    controller && SDL_GetGamepadType(controller->gamepad) == SDL_GAMEPAD_TYPE_PS5) {
-                    /* Cycle PS5 audio routing when the microphone button is pressed */
-                    if (event.gbutton.button == SDL_GAMEPAD_BUTTON_MISC1) {
-                        CyclePS5AudioRoute(controller);
-                    }
+        if (display_mode == CONTROLLER_MODE_TESTING) {
+            if (event->type == SDL_EVENT_GAMEPAD_BUTTON_DOWN &&
+                controller && SDL_GetGamepadType(controller->gamepad) == SDL_GAMEPAD_TYPE_PS5) {
+                /* Cycle PS5 audio routing when the microphone button is pressed */
+                if (event->gbutton.button == SDL_GAMEPAD_BUTTON_MISC1) {
+                    CyclePS5AudioRoute(controller);
+                }
 
-                    /* Cycle PS5 trigger effects when the triangle button is pressed */
-                    if (event.gbutton.button == SDL_GAMEPAD_BUTTON_NORTH) {
-                        CyclePS5TriggerEffect(controller);
-                    }
+                /* Cycle PS5 trigger effects when the triangle button is pressed */
+                if (event->gbutton.button == SDL_GAMEPAD_BUTTON_NORTH) {
+                    CyclePS5TriggerEffect(controller);
                 }
             }
-            break;
+        }
+        break;
 
-        case SDL_EVENT_MOUSE_BUTTON_DOWN:
-            if (virtual_joystick && controller && controller->joystick == virtual_joystick) {
-                VirtualGamepadMouseDown(event.button.x, event.button.y);
-            }
-            UpdateButtonHighlights(event.button.x, event.button.y, event.button.down);
-            break;
+    case SDL_EVENT_MOUSE_BUTTON_DOWN:
+        if (virtual_joystick && controller && controller->joystick == virtual_joystick) {
+            VirtualGamepadMouseDown(event->button.x, event->button.y);
+        }
+        UpdateButtonHighlights(event->button.x, event->button.y, event->button.down);
+        break;
 
-        case SDL_EVENT_MOUSE_BUTTON_UP:
-            if (virtual_joystick && controller && controller->joystick == virtual_joystick) {
-                VirtualGamepadMouseUp(event.button.x, event.button.y);
-            }
+    case SDL_EVENT_MOUSE_BUTTON_UP:
+        if (virtual_joystick && controller && controller->joystick == virtual_joystick) {
+            VirtualGamepadMouseUp(event->button.x, event->button.y);
+        }
 
-            if (display_mode == CONTROLLER_MODE_TESTING) {
-                if (GamepadButtonContains(setup_mapping_button, event.button.x, event.button.y)) {
-                    SetDisplayMode(CONTROLLER_MODE_BINDING);
+        if (display_mode == CONTROLLER_MODE_TESTING) {
+            if (GamepadButtonContains(setup_mapping_button, event->button.x, event->button.y)) {
+                SetDisplayMode(CONTROLLER_MODE_BINDING);
+            }
+        } else if (display_mode == CONTROLLER_MODE_BINDING) {
+            if (GamepadButtonContains(done_mapping_button, event->button.x, event->button.y)) {
+                if (controller->mapping) {
+                    SDL_Log("Mapping complete:\n");
+                    SDL_Log("%s\n", controller->mapping);
                 }
-            } else if (display_mode == CONTROLLER_MODE_BINDING) {
-                if (GamepadButtonContains(done_mapping_button, event.button.x, event.button.y)) {
-                    if (controller->mapping) {
-                        SDL_Log("Mapping complete:\n");
-                        SDL_Log("%s\n", controller->mapping);
-                    }
-                    SetDisplayMode(CONTROLLER_MODE_TESTING);
-                } else if (GamepadButtonContains(cancel_button, event.button.x, event.button.y)) {
-                    CancelMapping();
-                } else if (GamepadButtonContains(clear_button, event.button.x, event.button.y)) {
-                    ClearMapping();
-                } else if (controller->has_bindings &&
-                           GamepadButtonContains(copy_button, event.button.x, event.button.y)) {
-                    CopyMapping();
-                } else if (GamepadButtonContains(paste_button, event.button.x, event.button.y)) {
-                    PasteMapping();
-                } else if (title_pressed) {
-                    SetCurrentBindingElement(SDL_GAMEPAD_ELEMENT_NAME, false);
-                } else if (type_pressed) {
-                    SetCurrentBindingElement(SDL_GAMEPAD_ELEMENT_TYPE, false);
-                } else if (binding_element == SDL_GAMEPAD_ELEMENT_TYPE) {
-                    int type = GetGamepadTypeDisplayAt(gamepad_type, event.button.x, event.button.y);
-                    if (type != SDL_GAMEPAD_TYPE_UNSELECTED) {
-                        CommitGamepadType((SDL_GamepadType)type);
-                        StopBinding();
-                    }
-                } else {
-                    int gamepad_element = SDL_GAMEPAD_ELEMENT_INVALID;
-                    char *joystick_element;
+                SetDisplayMode(CONTROLLER_MODE_TESTING);
+            } else if (GamepadButtonContains(cancel_button, event->button.x, event->button.y)) {
+                CancelMapping();
+            } else if (GamepadButtonContains(clear_button, event->button.x, event->button.y)) {
+                ClearMapping();
+            } else if (controller->has_bindings &&
+                       GamepadButtonContains(copy_button, event->button.x, event->button.y)) {
+                CopyMapping();
+            } else if (GamepadButtonContains(paste_button, event->button.x, event->button.y)) {
+                PasteMapping();
+            } else if (title_pressed) {
+                SetCurrentBindingElement(SDL_GAMEPAD_ELEMENT_NAME, false);
+            } else if (type_pressed) {
+                SetCurrentBindingElement(SDL_GAMEPAD_ELEMENT_TYPE, false);
+            } else if (binding_element == SDL_GAMEPAD_ELEMENT_TYPE) {
+                int type = GetGamepadTypeDisplayAt(gamepad_type, event->button.x, event->button.y);
+                if (type != SDL_GAMEPAD_TYPE_UNSELECTED) {
+                    CommitGamepadType((SDL_GamepadType)type);
+                    StopBinding();
+                }
+            } else {
+                int gamepad_element = SDL_GAMEPAD_ELEMENT_INVALID;
+                char *joystick_element;
 
-                    if (controller->joystick != virtual_joystick) {
-                        gamepad_element = GetGamepadImageElementAt(image, event.button.x, event.button.y);
-                    }
-                    if (gamepad_element == SDL_GAMEPAD_ELEMENT_INVALID) {
-                        gamepad_element = GetGamepadDisplayElementAt(gamepad_elements, controller->gamepad, event.button.x, event.button.y);
-                    }
-                    if (gamepad_element != SDL_GAMEPAD_ELEMENT_INVALID) {
-                        /* Set this to false if you don't want to start the binding flow at this point */
-                        const bool should_start_flow = true;
-                        SetCurrentBindingElement(gamepad_element, should_start_flow);
-                    }
+                if (controller->joystick != virtual_joystick) {
+                    gamepad_element = GetGamepadImageElementAt(image, event->button.x, event->button.y);
+                }
+                if (gamepad_element == SDL_GAMEPAD_ELEMENT_INVALID) {
+                    gamepad_element = GetGamepadDisplayElementAt(gamepad_elements, controller->gamepad, event->button.x, event->button.y);
+                }
+                if (gamepad_element != SDL_GAMEPAD_ELEMENT_INVALID) {
+                    /* Set this to false if you don't want to start the binding flow at this point */
+                    const bool should_start_flow = true;
+                    SetCurrentBindingElement(gamepad_element, should_start_flow);
+                }
 
-                    joystick_element = GetJoystickDisplayElementAt(joystick_elements, controller->joystick, event.button.x, event.button.y);
-                    if (joystick_element) {
-                        CommitBindingElement(joystick_element, true);
-                        SDL_free(joystick_element);
-                    }
+                joystick_element = GetJoystickDisplayElementAt(joystick_elements, controller->joystick, event->button.x, event->button.y);
+                if (joystick_element) {
+                    CommitBindingElement(joystick_element, true);
+                    SDL_free(joystick_element);
                 }
             }
-            UpdateButtonHighlights(event.button.x, event.button.y, event.button.down);
-            break;
+        }
+        UpdateButtonHighlights(event->button.x, event->button.y, event->button.down);
+        break;
 
-        case SDL_EVENT_MOUSE_MOTION:
-            if (virtual_joystick && controller && controller->joystick == virtual_joystick) {
-                VirtualGamepadMouseMotion(event.motion.x, event.motion.y);
-            }
-            UpdateButtonHighlights(event.motion.x, event.motion.y, event.motion.state ? true : false);
-            break;
+    case SDL_EVENT_MOUSE_MOTION:
+        if (virtual_joystick && controller && controller->joystick == virtual_joystick) {
+            VirtualGamepadMouseMotion(event->motion.x, event->motion.y);
+        }
+        UpdateButtonHighlights(event->motion.x, event->motion.y, event->motion.state ? true : false);
+        break;
 
-        case SDL_EVENT_KEY_DOWN:
-            if (display_mode == CONTROLLER_MODE_TESTING) {
-                if (event.key.key >= SDLK_0 && event.key.key <= SDLK_9) {
-                    if (controller && controller->gamepad) {
-                        int player_index = (event.key.key - SDLK_0);
+    case SDL_EVENT_KEY_DOWN:
+        if (display_mode == CONTROLLER_MODE_TESTING) {
+            if (event->key.key >= SDLK_0 && event->key.key <= SDLK_9) {
+                if (controller && controller->gamepad) {
+                    int player_index = (event->key.key - SDLK_0);
 
-                        SDL_SetGamepadPlayerIndex(controller->gamepad, player_index);
-                    }
-                    break;
-                } else if (event.key.key == SDLK_A) {
-                    OpenVirtualGamepad();
-                } else if (event.key.key == SDLK_D) {
-                    CloseVirtualGamepad();
-                } else if (event.key.key == SDLK_R && (event.key.mod & SDL_KMOD_CTRL)) {
-                    SDL_ReloadGamepadMappings();
-                } else if (event.key.key == SDLK_ESCAPE) {
-                    done = true;
-                }
-            } else if (display_mode == CONTROLLER_MODE_BINDING) {
-                if (event.key.key == SDLK_C && (event.key.mod & SDL_KMOD_CTRL)) {
-                    if (binding_element == SDL_GAMEPAD_ELEMENT_NAME) {
-                        CopyControllerName();
-                    } else {
-                        CopyMapping();
-                    }
-                } else if (event.key.key == SDLK_V && (event.key.mod & SDL_KMOD_CTRL)) {
-                    if (binding_element == SDL_GAMEPAD_ELEMENT_NAME) {
-                        ClearControllerName();
-                        PasteControllerName();
-                    } else {
-                        PasteMapping();
-                    }
-                } else if (event.key.key == SDLK_X && (event.key.mod & SDL_KMOD_CTRL)) {
-                    if (binding_element == SDL_GAMEPAD_ELEMENT_NAME) {
-                        CopyControllerName();
-                        ClearControllerName();
-                    } else {
-                        CopyMapping();
-                        ClearMapping();
-                    }
-                } else if (event.key.key == SDLK_SPACE) {
-                    if (binding_element != SDL_GAMEPAD_ELEMENT_NAME) {
-                        ClearBinding();
-                    }
-                } else if (event.key.key == SDLK_BACKSPACE) {
-                    if (binding_element == SDL_GAMEPAD_ELEMENT_NAME) {
-                        BackspaceControllerName();
-                    }
-                } else if (event.key.key == SDLK_RETURN) {
-                    if (binding_element == SDL_GAMEPAD_ELEMENT_NAME) {
-                        StopBinding();
-                    }
-                } else if (event.key.key == SDLK_ESCAPE) {
-                    if (binding_element != SDL_GAMEPAD_ELEMENT_INVALID) {
-                        StopBinding();
-                    } else {
-                        CancelMapping();
-                    }
+                    SDL_SetGamepadPlayerIndex(controller->gamepad, player_index);
                 }
+                break;
+            } else if (event->key.key == SDLK_A) {
+                OpenVirtualGamepad();
+            } else if (event->key.key == SDLK_D) {
+                CloseVirtualGamepad();
+            } else if (event->key.key == SDLK_R && (event->key.mod & SDL_KMOD_CTRL)) {
+                SDL_ReloadGamepadMappings();
+            } else if (event->key.key == SDLK_ESCAPE) {
+                done = true;
             }
-            break;
-        case SDL_EVENT_TEXT_INPUT:
-            if (display_mode == CONTROLLER_MODE_BINDING) {
+        } else if (display_mode == CONTROLLER_MODE_BINDING) {
+            if (event->key.key == SDLK_C && (event->key.mod & SDL_KMOD_CTRL)) {
+                if (binding_element == SDL_GAMEPAD_ELEMENT_NAME) {
+                    CopyControllerName();
+                } else {
+                    CopyMapping();
+                }
+            } else if (event->key.key == SDLK_V && (event->key.mod & SDL_KMOD_CTRL)) {
+                if (binding_element == SDL_GAMEPAD_ELEMENT_NAME) {
+                    ClearControllerName();
+                    PasteControllerName();
+                } else {
+                    PasteMapping();
+                }
+            } else if (event->key.key == SDLK_X && (event->key.mod & SDL_KMOD_CTRL)) {
+                if (binding_element == SDL_GAMEPAD_ELEMENT_NAME) {
+                    CopyControllerName();
+                    ClearControllerName();
+                } else {
+                    CopyMapping();
+                    ClearMapping();
+                }
+            } else if (event->key.key == SDLK_SPACE) {
+                if (binding_element != SDL_GAMEPAD_ELEMENT_NAME) {
+                    ClearBinding();
+                }
+            } else if (event->key.key == SDLK_BACKSPACE) {
+                if (binding_element == SDL_GAMEPAD_ELEMENT_NAME) {
+                    BackspaceControllerName();
+                }
+            } else if (event->key.key == SDLK_RETURN) {
                 if (binding_element == SDL_GAMEPAD_ELEMENT_NAME) {
-                    AddControllerNameText(event.text.text);
+                    StopBinding();
+                }
+            } else if (event->key.key == SDLK_ESCAPE) {
+                if (binding_element != SDL_GAMEPAD_ELEMENT_INVALID) {
+                    StopBinding();
+                } else {
+                    CancelMapping();
                 }
             }
-            break;
-        case SDL_EVENT_QUIT:
-            done = true;
-            break;
-        default:
-            break;
         }
+        break;
+    case SDL_EVENT_TEXT_INPUT:
+        if (display_mode == CONTROLLER_MODE_BINDING) {
+            if (binding_element == SDL_GAMEPAD_ELEMENT_NAME) {
+                AddControllerNameText(event->text.text);
+            }
+        }
+        break;
+    case SDL_EVENT_QUIT:
+        done = true;
+        break;
+    default:
+        break;
+    }
+
+    if (done) {
+        return SDL_APP_SUCCESS;
+    } else {
+        return SDL_APP_CONTINUE;
+    }
+}
+
+SDL_AppResult SDLCALL SDL_AppIterate(void *appstate)
+{
+    /* If we have a virtual controller, send a virtual accelerometer sensor reading */
+    if (virtual_joystick) {
+        float data[3] = { 0.0f, SDL_STANDARD_GRAVITY, 0.0f };
+        SDL_SendJoystickVirtualSensorData(virtual_joystick, SDL_SENSOR_ACCEL, SDL_GetTicksNS(), data, SDL_arraysize(data));
     }
 
     /* Wait 30 ms for joystick events to stop coming in,
@@ -1982,14 +1985,10 @@ static void loop(void *arg)
     SDL_Delay(16);
     SDL_RenderPresent(screen);
 
-#ifdef SDL_PLATFORM_EMSCRIPTEN
-    if (done) {
-        emscripten_cancel_main_loop();
-    }
-#endif
+    return SDL_APP_CONTINUE;
 }
 
-int main(int argc, char *argv[])
+SDL_AppResult SDLCALL SDL_AppInit(void **appstate, int argc, char *argv[])
 {
     bool show_mappings = false;
     int i;
@@ -1997,12 +1996,11 @@ int main(int argc, char *argv[])
     int screen_width, screen_height;
     SDL_FRect area;
     int gamepad_index = -1;
-    SDLTest_CommonState *state;
 
     /* Initialize test framework */
     state = SDLTest_CommonCreateState(argv, 0);
     if (!state) {
-        return 1;
+        return SDL_APP_FAILURE;
     }
 
     SDL_SetHint(SDL_HINT_JOYSTICK_HIDAPI, "1");
@@ -2038,7 +2036,7 @@ int main(int argc, char *argv[])
         if (consumed <= 0) {
             static const char *options[] = { "[--mappings]", "[--virtual]", "[index]", NULL };
             SDLTest_CommonLogUsage(state, argv[0], options);
-            return 1;
+            return SDL_APP_FAILURE;
         }
 
         i += consumed;
@@ -2050,7 +2048,7 @@ int main(int argc, char *argv[])
     /* Initialize SDL (Note: video is required to start event loop) */
     if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK | SDL_INIT_GAMEPAD)) {
         SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't initialize SDL: %s\n", SDL_GetError());
-        return 1;
+        return SDL_APP_FAILURE;
     }
 
     SDL_AddGamepadMappingsFromFile("gamecontrollerdb.txt");
@@ -2077,14 +2075,14 @@ int main(int argc, char *argv[])
     window = SDL_CreateWindow("SDL Controller Test", screen_width, screen_height, 0);
     if (!window) {
         SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create window: %s\n", SDL_GetError());
-        return 2;
+        return SDL_APP_FAILURE;
     }
 
     screen = SDL_CreateRenderer(window, NULL);
     if (!screen) {
         SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Couldn't create renderer: %s\n", SDL_GetError());
         SDL_DestroyWindow(window);
-        return 2;
+        return SDL_APP_FAILURE;
     }
 
     SDL_SetRenderDrawColor(screen, 0x00, 0x00, 0x00, SDL_ALPHA_OPAQUE);
@@ -2110,7 +2108,7 @@ int main(int argc, char *argv[])
     if (!image) {
         SDL_DestroyRenderer(screen);
         SDL_DestroyWindow(window);
-        return 2;
+        return SDL_APP_FAILURE;
     }
     SetGamepadImagePosition(image, PANEL_WIDTH + PANEL_SPACING, TITLE_HEIGHT);
 
@@ -2178,7 +2176,7 @@ int main(int argc, char *argv[])
     SetGamepadButtonArea(done_mapping_button, &area);
 
     /* Process the initial gamepad list */
-    loop(NULL);
+    SDL_AppIterate(NULL);
 
     if (gamepad_index < num_controllers) {
         SetController(controllers[gamepad_index].id);
@@ -2186,15 +2184,11 @@ int main(int argc, char *argv[])
         SetController(controllers[0].id);
     }
 
-    /* Loop, getting gamepad events! */
-#ifdef SDL_PLATFORM_EMSCRIPTEN
-    emscripten_set_main_loop_arg(loop, NULL, 0, 1);
-#else
-    while (!done) {
-        loop(NULL);
-    }
-#endif
+    return SDL_APP_CONTINUE;
+}
 
+void SDLCALL SDL_AppQuit(void *appstate, SDL_AppResult result)
+{
     CloseVirtualGamepad();
     while (num_controllers > 0) {
         HandleGamepadRemoved(controllers[0].id);
@@ -2217,5 +2211,4 @@ int main(int argc, char *argv[])
     SDL_DestroyWindow(window);
     SDL_Quit();
     SDLTest_CommonDestroyState(state);
-    return 0;
 }