Skip to content

Commit

Permalink
audio: Prefer Pipewire over Pulseaudio if the pipewire-pulse service …
Browse files Browse the repository at this point in the history
…is running

Use DBus to query Systemd to check if the pipewire-pulse service is in the "running" state. If it is, then it is certain that Pipewire is being used instead of Pulseaudio as the preferred system mixer.

If DBus support is not enabled or Systemd is not being used on the underlying system, this check will simply fail and the standard driver order will be tested.
  • Loading branch information
Kontrabant authored and icculus committed Apr 12, 2024
1 parent b6cb63a commit 60f2618
Show file tree
Hide file tree
Showing 5 changed files with 136 additions and 5 deletions.
35 changes: 32 additions & 3 deletions src/audio/SDL_audio.c
Expand Up @@ -28,6 +28,9 @@
// Available audio drivers
static const AudioBootStrap *const bootstrap[] = {
#ifdef SDL_AUDIO_DRIVER_PULSEAUDIO
#ifdef SDL_AUDIO_DRIVER_PIPEWIRE
&PIPEWIRE_PREFERRED_bootstrap,
#endif
&PULSEAUDIO_bootstrap,
#endif
#ifdef SDL_AUDIO_DRIVER_PIPEWIRE
Expand Down Expand Up @@ -98,15 +101,41 @@ static const AudioBootStrap *const bootstrap[] = {

static SDL_AudioDriver current_audio;

// Deduplicated list of audio bootstrap drivers.
static const AudioBootStrap *deduped_bootstrap[SDL_arraysize(bootstrap) - 1];

int SDL_GetNumAudioDrivers(void)
{
return SDL_arraysize(bootstrap) - 1;
static int num_drivers = -1;

if (num_drivers >= 0) {
return num_drivers;
}

num_drivers = 0;

// Build a list of unique audio drivers.
for (int i = 0; bootstrap[i] != NULL; ++i) {
SDL_bool duplicate = SDL_FALSE;
for (int j = 0; j < i; ++j) {
if (SDL_strcmp(bootstrap[i]->name, bootstrap[j]->name) == 0) {
duplicate = SDL_TRUE;
break;
}
}

if (!duplicate) {
deduped_bootstrap[num_drivers++] = bootstrap[i];
}
}

return num_drivers;
}

const char *SDL_GetAudioDriver(int index)
{
if (index >= 0 && index < SDL_GetNumAudioDrivers()) {
return bootstrap[index]->name;
return deduped_bootstrap[index]->name;
}
return NULL;
}
Expand Down Expand Up @@ -887,8 +916,8 @@ int SDL_InitAudio(const char *driver_name)
current_audio.name = bootstrap[i]->name;
current_audio.desc = bootstrap[i]->desc;
initialized = SDL_TRUE;
break;
}
break;
}
}

Expand Down
1 change: 1 addition & 0 deletions src/audio/SDL_sysaudio.h
Expand Up @@ -335,6 +335,7 @@ typedef struct AudioBootStrap
} AudioBootStrap;

// Not all of these are available in a given build. Use #ifdefs, etc.
extern AudioBootStrap PIPEWIRE_PREFERRED_bootstrap;
extern AudioBootStrap PIPEWIRE_bootstrap;
extern AudioBootStrap PULSEAUDIO_bootstrap;
extern AudioBootStrap ALSA_bootstrap;
Expand Down
34 changes: 32 additions & 2 deletions src/audio/pipewire/SDL_pipewire.c
Expand Up @@ -29,6 +29,17 @@
#include <spa/param/audio/format-utils.h>
#include <spa/utils/json.h>

#include "../../core/linux/SDL_dbus.h"

static SDL_bool CheckPipewirePulseService()
{
#ifdef SDL_USE_LIBDBUS
return SDL_DBus_QuerySystemdUnitRunning("pipewire-pulse.service", SDL_TRUE);
#else
return SDL_FALSE;
#endif
}

/*
* The following keys are defined for compatibility when building against older versions of Pipewire
* prior to their introduction and can be removed if the minimum required Pipewire build version is
Expand Down Expand Up @@ -1251,7 +1262,7 @@ static void PIPEWIRE_Deinitialize(void)
}
}

static SDL_bool PIPEWIRE_Init(SDL_AudioDriverImpl *impl)
static SDL_bool PipewireInitialize(SDL_AudioDriverImpl *impl)
{
if (!pipewire_initialized) {
if (init_pipewire_library() < 0) {
Expand Down Expand Up @@ -1282,6 +1293,25 @@ static SDL_bool PIPEWIRE_Init(SDL_AudioDriverImpl *impl)
return SDL_TRUE;
}

AudioBootStrap PIPEWIRE_bootstrap = { "pipewire", "Pipewire", PIPEWIRE_Init, SDL_FALSE };
static SDL_bool PIPEWIRE_PREFERRED_Init(SDL_AudioDriverImpl *impl)
{
if (CheckPipewirePulseService()) {
return PipewireInitialize(impl);
}

return SDL_FALSE;
}

static SDL_bool PIPEWIRE_Init(SDL_AudioDriverImpl *impl)
{
return PipewireInitialize(impl);
}

AudioBootStrap PIPEWIRE_PREFERRED_bootstrap = {
"pipewire", "Pipewire", PIPEWIRE_PREFERRED_Init, SDL_FALSE
};
AudioBootStrap PIPEWIRE_bootstrap = {
"pipewire", "Pipewire", PIPEWIRE_Init, SDL_FALSE
};

#endif // SDL_AUDIO_DRIVER_PIPEWIRE
69 changes: 69 additions & 0 deletions src/core/linux/SDL_dbus.c
Expand Up @@ -632,4 +632,73 @@ char **SDL_DBus_DocumentsPortalRetrieveFiles(const char *key, int *path_count)
return NULL;
}

/* Check to see if a Systemd unit exists and is currently running. */
SDL_bool SDL_DBus_QuerySystemdUnitRunning(const char *unit_name, SDL_bool user_unit)
{
const char *path, *prop;
DBusError err;
SDL_bool running = SDL_FALSE;

/* Make sure we have a connection to the dbus session bus */
if (!SDL_DBus_GetContext() || !dbus.session_conn) {
/* We either cannot connect to the session bus or were unable to
* load the D-Bus library at all. */
return SDL_FALSE;
}

/* Make sure the appropriate bus is available. */
if ((user_unit && !dbus.session_conn) || (!user_unit && !dbus.system_conn)) {
return SDL_FALSE;
}

dbus.error_init(&err);

/* Get the object path for the unit. */
DBusMessage *method = dbus.message_new_method_call("org.freedesktop.systemd1",
"/org/freedesktop/systemd1",
"org.freedesktop.systemd1.Manager",
"GetUnit");
if (!method) {
return SDL_FALSE;
}

if (!dbus.message_append_args(method, DBUS_TYPE_STRING, &unit_name, DBUS_TYPE_INVALID)) {
SDL_OutOfMemory();
dbus.message_unref(method);
return SDL_FALSE;
}

DBusMessage *reply = dbus.connection_send_with_reply_and_block(user_unit ? dbus.session_conn : dbus.system_conn, method, DBUS_TIMEOUT_USE_DEFAULT, &err);
dbus.message_unref(method);
if (!reply) {
if (dbus.error_is_set(&err)) {
SDL_SetError("%s: %s", err.name, err.message);
dbus.error_free(&err);
}
return SDL_FALSE;
}

DBusMessageIter reply_iter;
if (!dbus.message_iter_init(reply, &reply_iter)) {
goto done;
}

if (dbus.message_iter_get_arg_type(&reply_iter) != DBUS_TYPE_OBJECT_PATH) {
goto done;
}

dbus.message_iter_get_basic(&reply_iter, &path);

/* We want to know the substate of the unit, which should be the string "running". */
if (SDL_DBus_QueryPropertyOnConnection(user_unit ? dbus.session_conn : dbus.system_conn,
"org.freedesktop.systemd1", path, "org.freedesktop.systemd1.Unit",
"SubState", DBUS_TYPE_STRING, &prop)) {
running = SDL_strcmp(prop, "running") == 0;
}

done:
dbus.message_unref(reply);
return running;
}

#endif
2 changes: 2 additions & 0 deletions src/core/linux/SDL_dbus.h
Expand Up @@ -109,6 +109,8 @@ extern char *SDL_DBus_GetLocalMachineId(void);

extern char **SDL_DBus_DocumentsPortalRetrieveFiles(const char *key, int *files_count);

extern SDL_bool SDL_DBus_QuerySystemdUnitRunning(const char *unit_name, SDL_bool user_unit);

#endif /* HAVE_DBUS_DBUS_H */

#endif /* SDL_dbus_h_ */

0 comments on commit 60f2618

Please sign in to comment.