This is an automated email from the git hooks/post-receive script.

git pushed a commit to branch overline-textgrid
in repository efl.

View the commit online.

commit cee1666531e729b84cdd4198abe89d7e1e73c948
Author: Carsten Haitzler <[email protected]>
AuthorDate: Fri Sep 11 17:13:49 2026 +0100

    ecoe audio: make auto pobe find pulse reliably first time
    
    have to wait longer. might still be starting up.
---
 src/lib/ecore_audio/ecore_audio_obj_out_pipewire.c |  80 ++++++++-------
 src/lib/ecore_audio/ecore_audio_obj_out_pulse.c    |  49 ++++-----
 src/lib/ecore_audio/ecore_audio_obj_out_system.c   | 111 +++++++++++++--------
 src/lib/ecore_audio/ecore_audio_out_system.eo      |  14 ++-
 4 files changed, 146 insertions(+), 108 deletions(-)

diff --git a/src/lib/ecore_audio/ecore_audio_obj_out_pipewire.c b/src/lib/ecore_audio/ecore_audio_obj_out_pipewire.c
index da430d099e..e2b0069c6f 100644
--- a/src/lib/ecore_audio/ecore_audio_obj_out_pipewire.c
+++ b/src/lib/ecore_audio/ecore_audio_obj_out_pipewire.c
@@ -11,11 +11,48 @@
 
 #define MY_CLASS ECORE_AUDIO_OUT_PIPEWIRE_CLASS
 
+typedef struct _Ecore_Audio_Out_Pipewire_Data
+{
+  Eo *obj;
+  struct pw_loop *loop;
+  struct pw_context *context;
+  struct pw_core *core;
+  struct spa_hook listener;
+  Ecore_Fd_Handler *handler;
+  Ecore_Job *state_job;
+  Ecore_Timer *timeout;
+  Eina_List *streams;
+  int sync;
+  Eina_Bool init : 1;
+  Eina_Bool failed : 1;
+} Ecore_Audio_Out_Pipewire_Data;
+
+typedef struct _Ecore_Audio_Pipewire_Stream
+{
+  Ecore_Audio_Out_Pipewire_Data *output;
+  Eo *input;
+  struct pw_stream *stream;
+  struct spa_hook listener;
+  unsigned int stride;
+  Eina_Bool processing : 1;
+  Eina_Bool detached : 1;
+  Eina_Bool ended : 1;
+} Ecore_Audio_Pipewire_Stream;
+
+typedef struct _Ecore_Audio_Pipewire_Probe
+{
+  int sync;
+  Eina_Bool done : 1;
+  Eina_Bool available : 1;
+} Ecore_Audio_Pipewire_Probe;
+
 /* Keep the library loaded across ecore_audio_shutdown(): objects and deferred
  * cleanup jobs may still call PipeWire.
  */
 Ecore_Audio_Lib_Pipewire *ecore_audio_pipewire_lib = NULL;
 
+static Eina_Bool _probe_available = EINA_FALSE;
+
 Eina_Bool
 ecore_audio_pipewire_lib_load(void)
 {
@@ -117,16 +154,6 @@ ecore_audio_pipewire_lib_unload(void)
      }
 }
 
-static Eina_Bool _probe_checked;
-static Eina_Bool _probe_available;
-
-typedef struct
-{
-   int sync;
-   Eina_Bool done;
-   Eina_Bool available;
-} Ecore_Audio_Pipewire_Probe;
-
 static void
 _probe_done(void *data, uint32_t id, int seq)
 {
@@ -164,8 +191,7 @@ _ecore_audio_out_pipewire_probe(void)
    double deadline, remaining;
    int ret;
 
-   if (_probe_checked) return _probe_available;
-   _probe_checked = EINA_TRUE;
+   if (_probe_available) return EINA_TRUE;
    if (!EPW_LOAD()) return EINA_FALSE;
    EPW_CALL(pw_init)(NULL, NULL);
    loop = EPW_CALL(pw_loop_new)(NULL);
@@ -178,7 +204,7 @@ _ecore_audio_out_pipewire_probe(void)
    EPW_CALL(pw_core_add_listener)(core, &listener, &_probe_events, &probe);
    probe.sync = EPW_CALL(pw_core_sync)(core, PW_ID_CORE, 0);
    if (probe.sync < 0) goto disconnect;
-   deadline = ecore_time_get() + 0.5;
+   deadline = ecore_time_get() + 5.0;
    while (!probe.done)
      {
         remaining = deadline - ecore_time_get();
@@ -200,34 +226,6 @@ end:
    return _probe_available;
 }
 
-typedef struct
-{
-   Eo *obj;
-   struct pw_loop *loop;
-   struct pw_context *context;
-   struct pw_core *core;
-   struct spa_hook listener;
-   Ecore_Fd_Handler *handler;
-   Ecore_Job *state_job;
-   Ecore_Timer *timeout;
-   Eina_List *streams;
-   int sync;
-  Eina_Bool init : 1;
-  Eina_Bool failed : 1;
-} Ecore_Audio_Out_Pipewire_Data;
-
-typedef struct
-{
-   Ecore_Audio_Out_Pipewire_Data *output;
-   Eo *input;
-   struct pw_stream *stream;
-   struct spa_hook listener;
-   unsigned int stride;
-   Eina_Bool processing;
-   Eina_Bool detached;
-   Eina_Bool ended;
-} Ecore_Audio_Pipewire_Stream;
-
 static void
 _state_job(void *data)
 {
diff --git a/src/lib/ecore_audio/ecore_audio_obj_out_pulse.c b/src/lib/ecore_audio/ecore_audio_obj_out_pulse.c
index b873af94b6..bf8ca0ebe6 100644
--- a/src/lib/ecore_audio/ecore_audio_obj_out_pulse.c
+++ b/src/lib/ecore_audio/ecore_audio_obj_out_pulse.c
@@ -20,6 +20,15 @@
 #include <sys/types.h>
 #include <sys/socket.h>
 
+typedef struct _Ecore_Audio_Out_Pulse_Data
+{
+   pa_mainloop_api *api;
+   pa_context *context;
+   pa_context_state_t state;
+   Ecore_Job *state_job;
+   Eina_List *outputs;
+} Ecore_Audio_Out_Pulse_Data;
+
 extern pa_mainloop_api functable;
 
 #define MY_CLASS ECORE_AUDIO_OUT_PULSE_CLASS
@@ -127,8 +136,7 @@ ecore_audio_pulse_lib_unload(void)
      }
 }
 
-static Eina_Bool _probe_checked;
-static Eina_Bool _probe_available;
+static Eina_Bool _probe_available = EINA_FALSE;
 
 Eina_Bool
 _ecore_audio_out_pulse_probe(void)
@@ -138,8 +146,7 @@ _ecore_audio_out_pulse_probe(void)
    pa_context_state_t state;
    double deadline, remaining;
 
-   if (_probe_checked) return _probe_available;
-   _probe_checked = EINA_TRUE;
+   if (_probe_available) return EINA_TRUE;
    if (!EPA_LOAD()) return EINA_FALSE;
    loop = EPA_CALL(pa_mainloop_new)();
    if (!loop) return EINA_FALSE;
@@ -149,7 +156,7 @@ _ecore_audio_out_pulse_probe(void)
    /* Detect an existing server; do not start one merely to probe it. */
    if (EPA_CALL(pa_context_connect)(context, NULL, PA_CONTEXT_NOAUTOSPAWN,
                                     NULL) < 0) goto disconnect;
-   deadline = ecore_time_get() + 0.5;
+   deadline = ecore_time_get() + 5.0;
    for (;;)
      {
         state = EPA_CALL(pa_context_get_state)(context);
@@ -174,17 +181,6 @@ end:
    return _probe_available;
 }
 
-struct _Ecore_Audio_Out_Pulse_Data
-{
-   pa_mainloop_api *api;
-   pa_context *context;
-   pa_context_state_t state;
-   Ecore_Job *state_job;
-   Eina_List *outputs;
-};
-
-typedef struct _Ecore_Audio_Out_Pulse_Data Ecore_Audio_Out_Pulse_Data;
-
 EOLIAN static void
 _ecore_audio_out_pulse_ecore_audio_paused_set(Eo *eo_obj, Ecore_Audio_Out_Pulse_Data *pd EINA_UNUSED, Eina_Bool paused)
 {
@@ -231,7 +227,8 @@ _ecore_audio_out_pulse_ecore_audio_volume_set(Eo *eo_obj, Ecore_Audio_Out_Pulse_
     }
 }
 
-static void _write_cb(pa_stream *stream, size_t len, void *data)
+static void
+_write_cb(pa_stream *stream, size_t len, void *data)
 {
   Eo *in = data;
 
@@ -254,7 +251,8 @@ static void _write_cb(pa_stream *stream, size_t len, void *data)
     EPA_CALL(pa_stream_write)(stream, buf, bread, NULL, 0, PA_SEEK_RELATIVE);
 }
 
-static void _update_samplerate_cb(void *data EINA_UNUSED, const Efl_Event *event)
+static void
+_update_samplerate_cb(void *data EINA_UNUSED, const Efl_Event *event)
 {
   pa_stream *stream = NULL;
   int samplerate = 0;
@@ -268,7 +266,8 @@ static void _update_samplerate_cb(void *data EINA_UNUSED, const Efl_Event *event
   EPA_CALL(pa_operation_unref)(EPA_CALL(pa_stream_update_sample_rate)(stream, samplerate * speed, NULL, NULL));
 }
 
-static Eina_Bool _input_attach_internal(Eo *eo_obj, Eo *in)
+static Eina_Bool
+_input_attach_internal(Eo *eo_obj, Eo *in)
 {
   const char *name = NULL;
   pa_sample_spec ss;
@@ -315,7 +314,8 @@ static Eina_Bool _input_attach_internal(Eo *eo_obj, Eo *in)
   return ret;
 }
 
-static void _delayed_attach_cb(void *data, const Efl_Event *event)
+static void
+_delayed_attach_cb(void *data, const Efl_Event *event)
 {
   efl_event_callback_del(event->object, ECORE_AUDIO_OUT_PULSE_EVENT_CONTEXT_READY, _delayed_attach_cb, data);
 
@@ -350,7 +350,8 @@ _ecore_audio_out_pulse_ecore_audio_out_input_attach(Eo *eo_obj, Ecore_Audio_Out_
   return retval;
 }
 
-static void _drain_cb(pa_stream *stream, int success EINA_UNUSED, void *data EINA_UNUSED)
+static void
+_drain_cb(pa_stream *stream, int success EINA_UNUSED, void *data EINA_UNUSED)
 {
   if (!EPA_LOAD()) return;
   EPA_CALL(pa_stream_disconnect)(stream);
@@ -404,7 +405,8 @@ _ecore_audio_out_pulse_ecore_audio_out_input_detach(Eo *eo_obj, Ecore_Audio_Out_
   return EINA_TRUE;
 }
 
-static void _state_cb(pa_context *context, void *data)
+static void
+_state_cb(pa_context *context, void *data)
 {
    Eina_List *out, *tmp;
    Eo *eo_obj;
@@ -448,7 +450,8 @@ static void _state_cb(pa_context *context, void *data)
     }
 }
 
-static void _state_job(void *data)
+static void
+_state_job(void *data)
 {
    Ecore_Audio_Out_Pulse_Data *pd = data;
    if ((pd->state == PA_CONTEXT_FAILED) ||
diff --git a/src/lib/ecore_audio/ecore_audio_obj_out_system.c b/src/lib/ecore_audio/ecore_audio_obj_out_system.c
index 024a2714d1..e49c7604e8 100644
--- a/src/lib/ecore_audio/ecore_audio_obj_out_system.c
+++ b/src/lib/ecore_audio/ecore_audio_obj_out_system.c
@@ -9,35 +9,35 @@
 
 #define MY_CLASS ECORE_AUDIO_OUT_SYSTEM_CLASS
 
-typedef enum
+typedef enum _Ecore_Audio_System_Request_Type
 {
-   REQUEST_VOLUME,
-   REQUEST_PAUSED,
-   REQUEST_ATTACH,
-   REQUEST_DETACH,
-   REQUEST_VIO
+  REQUEST_VOLUME,
+  REQUEST_PAUSED,
+  REQUEST_ATTACH,
+  REQUEST_DETACH,
+  REQUEST_VIO
 } Ecore_Audio_System_Request_Type;
 
-typedef struct
+typedef struct _Ecore_Audio_System_Request
 {
-   Ecore_Audio_System_Request_Type type;
-   double volume;
-   Eina_Bool paused;
-   Eo *input;
-   Ecore_Audio_Vio *vio;
-   void *data;
-   efl_key_data_free_func free_func;
+  Ecore_Audio_System_Request_Type type;
+  double volume;
+  Eo *input;
+  Ecore_Audio_Vio *vio;
+  void *data;
+  efl_key_data_free_func free_func;
+  Eina_Bool paused : 1;
 } Ecore_Audio_System_Request;
 
-typedef struct
+typedef struct _Ecore_Audio_Out_System_Data
 {
-   Eo *backend;
-   Ecore_Job *ready_job;
-   Eina_List *requests;
-   Eina_List *pending;
-   Eina_Bool waiting;
-   Eina_Bool failed;
-   Eina_Bool replaying;
+  Eo *backend;
+  Ecore_Job *ready_job;
+  Eina_List *requests;
+  Eina_List *pending;
+  Eina_Bool waiting : 1;
+  Eina_Bool failed : 1;
+  Eina_Bool replaying : 1;
 } Ecore_Audio_Out_System_Data;
 
 /* Cache the getter, rather than an Eo class pointer which can become stale
@@ -48,10 +48,11 @@ typedef struct
 static const Efl_Class *(*_backend_class_get)(void) = ecore_audio_out_wasapi_class_get;
 #else
 static const Efl_Class *(*_backend_class_get)(void);
-static Eina_Bool _backend_checked;
-static Eina_Bool _backend_env_checked;
-static Eina_List *_waiting;
-static Ecore_Job *_probe_job;
+static Eina_Bool _backend_checked = EINA_FALSE;
+static Eina_Bool _backend_env_checked = EINA_FALSE;
+static Eina_List *_waiting = NULL;
+static Ecore_Job *_probe_job = NULL;
+static Ecore_Timer *_probe_timer = NULL;
 #endif
 
 static void
@@ -100,8 +101,9 @@ _context_fail_cb(void *data, const Efl_Event *event)
    pd->waiting = EINA_FALSE;
    pd->failed = EINA_TRUE;
    _requests_clear(pd);
-   efl_event_callback_call(data, ECORE_AUDIO_OUT_SYSTEM_EVENT_CONTEXT_FAIL,
-                          event ? event->info : NULL);
+   efl_event_callback_call(data,
+                           ECORE_AUDIO_OUT_SYSTEM_EVENT_CONTEXT_FAIL,
+                           event ? event->info : NULL);
    efl_unref(data);
 }
 
@@ -153,9 +155,11 @@ _context_ready_cb(void *data, const Efl_Event *event)
    pd->pending = eina_list_free(pd->pending);
    pd->waiting = EINA_FALSE;
    pd->replaying = EINA_FALSE;
-   efl_event_callback_call(data, success ? ECORE_AUDIO_OUT_SYSTEM_EVENT_CONTEXT_READY :
-                          ECORE_AUDIO_OUT_SYSTEM_EVENT_CONTEXT_FAIL,
-                          event ? event->info : NULL);
+   efl_event_callback_call(data,
+                           success ?
+                           ECORE_AUDIO_OUT_SYSTEM_EVENT_CONTEXT_READY :
+                           ECORE_AUDIO_OUT_SYSTEM_EVENT_CONTEXT_FAIL,
+                           event ? event->info : NULL);
    efl_unref(data);
 }
 
@@ -221,7 +225,8 @@ _backend_new(Eo *obj, Ecore_Audio_Out_System_Data *pd)
    if (!pd->backend) return EINA_FALSE;
    _backend_callbacks(obj, pd, EINA_TRUE);
 #ifdef _WIN32
-   efl_event_callback_add(pd->backend, ECORE_AUDIO_OUT_WASAPI_EVENT_STOP,
+   efl_event_callback_add(pd->backend,
+                          ECORE_AUDIO_OUT_WASAPI_EVENT_STOP,
                           _stop_cb, obj);
    /* WASAPI initializes synchronously and does not emit context,ready. */
    pd->ready_job = ecore_job_add(_ready_job, obj);
@@ -233,6 +238,18 @@ _backend_new(Eo *obj, Ecore_Audio_Out_System_Data *pd)
 }
 
 #ifndef _WIN32
+#if defined(HAVE_PULSE) || defined(HAVE_PIPEWIRE)
+static void _probe_finish(void *data);
+
+static Eina_Bool
+_probe_retry(void *data EINA_UNUSED)
+{
+   _probe_timer = NULL;
+   _probe_finish(NULL);
+   return ECORE_CALLBACK_CANCEL;
+}
+#endif
+
 static void
 _probe_finish(void *data EINA_UNUSED)
 {
@@ -248,7 +265,17 @@ _probe_finish(void *data EINA_UNUSED)
    if (!_backend_class_get && _ecore_audio_out_pipewire_probe())
      _backend_class_get = ecore_audio_out_pipewire_class_get;
 #endif
-   _backend_checked = EINA_TRUE;
+   /* A server may still be starting. Keep the outputs and their queued
+    * requests waiting, and cache only a successful automatic selection.
+    */
+   _backend_checked = !!_backend_class_get;
+#if defined(HAVE_PULSE) || defined(HAVE_PIPEWIRE)
+   if ((!_backend_checked) && (_waiting))
+     {
+        _probe_timer = ecore_timer_add(1.0, _probe_retry, NULL);
+        if (_probe_timer) return;
+     }
+#endif
 
    /* Callbacks may destroy other waiters, so remove each one before calling. */
    while (_waiting)
@@ -267,10 +294,10 @@ _backend_check(void)
 {
    const char *backend;
 
-   if (_backend_checked || _probe_job) return;
+   if (_backend_checked || _probe_job || _probe_timer) return;
    backend = _backend_env_checked ? NULL : getenv("ECORE_AUDIO_BACKEND");
    _backend_env_checked = EINA_TRUE;
-   if (backend && backend[0] && strcmp(backend, "auto"))
+   if ((backend) && (backend[0]) && (!!strcmp(backend, "auto")))
      {
         _backend_checked = EINA_TRUE;
 #ifdef HAVE_PIPEWIRE
@@ -319,11 +346,16 @@ _ecore_audio_out_system_efl_object_destructor(Eo *eo_obj, Ecore_Audio_Out_System
 {
 #ifndef _WIN32
    _waiting = eina_list_remove(_waiting, eo_obj);
-   if (!_waiting && _probe_job)
+   if ((!_waiting) && (_probe_job))
      {
         ecore_job_del(_probe_job);
         _probe_job = NULL;
      }
+   if ((!_waiting) && (_probe_timer))
+     {
+        ecore_timer_del(_probe_timer);
+        _probe_timer = NULL;
+     }
 #endif
    if (pd->ready_job) ecore_job_del(pd->ready_job);
    pd->failed = EINA_TRUE;
@@ -333,7 +365,8 @@ _ecore_audio_out_system_efl_object_destructor(Eo *eo_obj, Ecore_Audio_Out_System
      {
         _backend_callbacks(eo_obj, pd, EINA_FALSE);
 #ifdef _WIN32
-        efl_event_callback_del(pd->backend, ECORE_AUDIO_OUT_WASAPI_EVENT_STOP,
+        efl_event_callback_del(pd->backend,
+                               ECORE_AUDIO_OUT_WASAPI_EVENT_STOP,
                                _stop_cb, eo_obj);
 #endif
         efl_unref(pd->backend);
@@ -361,7 +394,7 @@ _ecore_audio_out_system_ecore_audio_volume_set(Eo *eo_obj, Ecore_Audio_Out_Syste
 EOLIAN static double
 _ecore_audio_out_system_ecore_audio_volume_get(const Eo *eo_obj, Ecore_Audio_Out_System_Data *pd)
 {
-   if (pd->waiting || !pd->backend)
+   if ((pd->waiting) || (!pd->backend))
      return ecore_audio_obj_volume_get(efl_super(eo_obj, MY_CLASS));
    return ecore_audio_obj_volume_get(pd->backend);
 }
@@ -385,7 +418,7 @@ _ecore_audio_out_system_ecore_audio_paused_set(Eo *eo_obj, Ecore_Audio_Out_Syste
 EOLIAN static Eina_Bool
 _ecore_audio_out_system_ecore_audio_paused_get(const Eo *eo_obj, Ecore_Audio_Out_System_Data *pd)
 {
-   if (pd->waiting || !pd->backend)
+   if ((pd->waiting) || (!pd->backend))
      return ecore_audio_obj_paused_get(efl_super(eo_obj, MY_CLASS));
    return ecore_audio_obj_paused_get(pd->backend);
 }
diff --git a/src/lib/ecore_audio/ecore_audio_out_system.eo b/src/lib/ecore_audio/ecore_audio_out_system.eo
index 5da1e939e6..52357d0563 100644
--- a/src/lib/ecore_audio/ecore_audio_out_system.eo
+++ b/src/lib/ecore_audio/ecore_audio_out_system.eo
@@ -3,19 +3,23 @@ class @beta Ecore.Audio.Out.System extends Ecore.Audio.Out
    [[Portable audio output using WASAPI on Windows and PulseAudio elsewhere,
      with PipeWire as a fallback when PulseAudio is unavailable.
 
-     Backend selection is shared by all system outputs and cached for the
-     lifetime of the process. On non-Windows platforms, the first output
+     Successful backend selection is shared by all system outputs and cached
+     for the lifetime of the process. On non-Windows platforms, the first output
      probes PulseAudio first, then PipeWire if PulseAudio cannot connect.
      Probes use native connections without constructing audio output objects,
-     and each backend caches its availability, including failed probes.
+     and each backend caches successful availability probes. If neither server
+     is available, detection retries once per second while outputs are waiting.
      Later outputs construct the selected backend directly, even if that
      backend subsequently fails. ECORE_AUDIO_BACKEND can select pipewire or
      pulseaudio (pulse is an alias) explicitly, disabling fallback. Unset,
      empty or auto uses detection. Unknown or unavailable explicit backends
      fail construction. The variable is read once and ignored on Windows.
 
-     Failed automatic detection emits context,fail. Construction fails if
-     an already selected backend cannot be created. Inputs are
+     Unsuccessful automatic detection keeps outputs and queued requests waiting
+     for a server to become available. If a retry cannot be scheduled or no
+     backend is compiled in, context,fail is emitted. Construction fails if
+     initial detection cannot be scheduled or an already selected backend
+     cannot be created. Inputs are
      attached to the owned backend, so Ecore.Audio.In.output returns that
      backend. Input lists and playback capabilities follow the backend.
      While detection or connection is pending, volume, pause, input and VIO

-- 
To stop receiving notification emails like this one, please contact
the administrator of this repository.

Reply via email to