SDL: pulseaudio: More workarounds for extremely old Pulse installs.

From f24551f6d1d09bdc02ae65ee7bac186b717d4800 Mon Sep 17 00:00:00 2001
From: "Ryan C. Gordon" <[EMAIL REDACTED]>
Date: Thu, 28 Sep 2023 11:53:00 -0400
Subject: [PATCH] pulseaudio: More workarounds for extremely old Pulse
 installs.

---
 src/audio/pulseaudio/SDL_pulseaudio.c | 17 ++++++++++++++---
 1 file changed, 14 insertions(+), 3 deletions(-)

diff --git a/src/audio/pulseaudio/SDL_pulseaudio.c b/src/audio/pulseaudio/SDL_pulseaudio.c
index 4e743cd08e13..a6333bf4930f 100644
--- a/src/audio/pulseaudio/SDL_pulseaudio.c
+++ b/src/audio/pulseaudio/SDL_pulseaudio.c
@@ -190,7 +190,6 @@ static int load_pulseaudio_syms(void)
     SDL_PULSEAUDIO_SYM(pa_threaded_mainloop_free);
     SDL_PULSEAUDIO_SYM(pa_operation_get_state);
     SDL_PULSEAUDIO_SYM(pa_operation_cancel);
-    SDL_PULSEAUDIO_SYM(pa_operation_set_state_callback);
     SDL_PULSEAUDIO_SYM(pa_operation_unref);
     SDL_PULSEAUDIO_SYM(pa_context_new);
     SDL_PULSEAUDIO_SYM(pa_context_set_state_callback);
@@ -226,10 +225,16 @@ static int load_pulseaudio_syms(void)
 
     /* optional */
 #ifdef SDL_AUDIO_DRIVER_PULSEAUDIO_DYNAMIC
-    load_pulseaudio_sym("pa_threaded_mainloop_set_name", (void **)(char *)&PULSEAUDIO_pa_threaded_mainloop_set_name);
+    load_pulseaudio_sym("pa_operation_set_state_callback", (void **)(char *)&PULSEAUDIO_pa_operation_set_state_callback);  // needs pulseaudio 4.0
+    load_pulseaudio_sym("pa_threaded_mainloop_set_name", (void **)(char *)&PULSEAUDIO_pa_threaded_mainloop_set_name);  // needs pulseaudio 5.0
 #elif (PA_PROTOCOL_VERSION >= 29)
+    PULSEAUDIO_pa_operation_set_state_callback = pa_operation_set_state_callback;
     PULSEAUDIO_pa_threaded_mainloop_set_name = pa_threaded_mainloop_set_name;
+#elif (PA_PROTOCOL_VERSION >= 28)
+    PULSEAUDIO_pa_operation_set_state_callback = pa_operation_set_state_callback;
+    PULSEAUDIO_pa_threaded_mainloop_set_name = NULL;
 #else
+    PULSEAUDIO_pa_operation_set_state_callback = NULL;
     PULSEAUDIO_pa_threaded_mainloop_set_name = NULL;
 #endif
 
@@ -278,7 +283,13 @@ static void WaitForPulseOperation(pa_operation *o)
     /* This checks for NO errors currently. Either fix that, check results elsewhere, or do things you don't care about. */
     SDL_assert(pulseaudio_threaded_mainloop != NULL);
     if (o) {
-        PULSEAUDIO_pa_operation_set_state_callback(o, OperationStateChangeCallback, NULL);
+        // note that if PULSEAUDIO_pa_operation_set_state_callback == NULL, then `o` must have a callback that will signal pulseaudio_threaded_mainloop.
+        // If not, on really old (earlier PulseAudio 4.0, from the year 2013!) installs, this call will block forever.
+        // On more modern installs, we won't ever block forever, and maybe be more efficient, thanks to pa_operation_set_state_callback.
+        // WARNING: at the time of this writing: the Steam Runtime is still on PulseAudio 1.1!
+        if (PULSEAUDIO_pa_operation_set_state_callback != NULL) {
+            PULSEAUDIO_pa_operation_set_state_callback(o, OperationStateChangeCallback, NULL);
+        }
         while (PULSEAUDIO_pa_operation_get_state(o) == PA_OPERATION_RUNNING) {
             PULSEAUDIO_pa_threaded_mainloop_wait(pulseaudio_threaded_mainloop);  /* this releases the lock and blocks on an internal condition variable. */
         }