Add SDL_HINT_AUDIO_DEVICE_APP_ICON_NAME

This commit is contained in:
Thomas J Faughnan Jr 2024-05-11 13:32:40 -04:00 committed by Ryan C. Gordon
parent 8bd0433966
commit ad166be1c5
3 changed files with 55 additions and 7 deletions

View File

@ -292,6 +292,28 @@ extern "C" {
*/
#define SDL_HINT_AUDIO_DEVICE_APP_NAME "SDL_AUDIO_DEVICE_APP_NAME"
/**
* Specify an application icon name for an audio device.
*
* Some audio backends (such as Pulseaudio and Pipewire) allow you to set an
* XDG icon name for your application. Among other things, this icon might show
* up in a system control panel that lets the user adjust the volume on specific
* audio streams instead of using one giant master volume slider. Note that this
* is unrelated to the icon used by the windowing system, which may be set with
* SDL_SetWindowIcon (or via desktop file on Wayland).
*
* Setting this to "" or leaving it unset will have SDL use a reasonable
* default, "applications-games", which is likely to be installed.
* See https://specifications.freedesktop.org/icon-theme-spec/icon-theme-spec-latest.html
* and https://specifications.freedesktop.org/icon-naming-spec/icon-naming-spec-latest.html
* for the relevant XDG icon specs.
*
* This hint should be set before an audio device is opened.
*
* \since This hint is available since SDL 3.0.0.
*/
#define SDL_HINT_AUDIO_DEVICE_APP_ICON_NAME "SDL_AUDIO_DEVICE_APP_ICON_NAME"
/**
* A variable controlling device buffer size.
*

View File

@ -1108,7 +1108,7 @@ static int PIPEWIRE_OpenDevice(SDL_AudioDevice *device)
const struct spa_pod *params = NULL;
struct SDL_PrivateAudioData *priv;
struct pw_properties *props;
const char *app_name, *app_id, *stream_name, *stream_role, *error;
const char *app_name, *icon_name, *app_id, *stream_name, *stream_role, *error;
Uint32 node_id = !device->handle ? PW_ID_ANY : PW_HANDLE_TO_ID(device->handle);
const SDL_bool iscapture = device->iscapture;
int res;
@ -1116,7 +1116,7 @@ static int PIPEWIRE_OpenDevice(SDL_AudioDevice *device)
// Clamp the period size to sane values
const int min_period = PW_MIN_SAMPLES * SPA_MAX(device->spec.freq / PW_BASE_CLOCK_RATE, 1);
// Get the hints for the application name, stream name and role
// Get the hints for the application name, icon name, stream name and role
app_name = SDL_GetHint(SDL_HINT_AUDIO_DEVICE_APP_NAME);
if (!app_name || *app_name == '\0') {
app_name = SDL_GetHint(SDL_HINT_APP_NAME);
@ -1125,6 +1125,11 @@ static int PIPEWIRE_OpenDevice(SDL_AudioDevice *device)
}
}
icon_name = SDL_GetHint(SDL_HINT_AUDIO_DEVICE_APP_ICON_NAME);
if (!icon_name || *icon_name == '\0') {
icon_name = "applications-games";
}
// App ID. Default to NULL if not available.
app_id = SDL_GetHint(SDL_HINT_APP_ID);
@ -1190,6 +1195,7 @@ static int PIPEWIRE_OpenDevice(SDL_AudioDevice *device)
PIPEWIRE_pw_properties_set(props, PW_KEY_MEDIA_CATEGORY, iscapture ? "Capture" : "Playback");
PIPEWIRE_pw_properties_set(props, PW_KEY_MEDIA_ROLE, stream_role);
PIPEWIRE_pw_properties_set(props, PW_KEY_APP_NAME, app_name);
PIPEWIRE_pw_properties_set(props, PW_KEY_APP_ICON_NAME, icon_name);
if (app_id) {
PIPEWIRE_pw_properties_set(props, PW_KEY_APP_ID, app_id);
}

View File

@ -66,6 +66,9 @@ static const char *(*PULSEAUDIO_pa_get_library_version)(void);
static pa_channel_map *(*PULSEAUDIO_pa_channel_map_init_auto)(
pa_channel_map *, unsigned, pa_channel_map_def_t);
static const char *(*PULSEAUDIO_pa_strerror)(int);
static pa_proplist *(*PULSEAUDIO_pa_proplist_new)(void);
static void (*PULSEAUDIO_pa_proplist_free)(pa_proplist *);
static int (*PULSEAUDIO_pa_proplist_sets)(pa_proplist *, const char *, const char *);
static pa_threaded_mainloop *(*PULSEAUDIO_pa_threaded_mainloop_new)(void);
static void (*PULSEAUDIO_pa_threaded_mainloop_set_name)(pa_threaded_mainloop *, const char *);
@ -84,8 +87,9 @@ static void (*PULSEAUDIO_pa_operation_set_state_callback)(pa_operation *, pa_ope
static void (*PULSEAUDIO_pa_operation_cancel)(pa_operation *);
static void (*PULSEAUDIO_pa_operation_unref)(pa_operation *);
static pa_context *(*PULSEAUDIO_pa_context_new)(pa_mainloop_api *,
const char *);
static pa_context *(*PULSEAUDIO_pa_context_new_with_proplist)(pa_mainloop_api *,
const char *,
const pa_proplist *);
static void (*PULSEAUDIO_pa_context_set_state_callback)(pa_context *, pa_context_notify_cb_t, void *);
static int (*PULSEAUDIO_pa_context_connect)(pa_context *, const char *,
pa_context_flags_t, const pa_spawn_api *);
@ -205,7 +209,7 @@ static int load_pulseaudio_syms(void)
SDL_PULSEAUDIO_SYM(pa_operation_get_state);
SDL_PULSEAUDIO_SYM(pa_operation_cancel);
SDL_PULSEAUDIO_SYM(pa_operation_unref);
SDL_PULSEAUDIO_SYM(pa_context_new);
SDL_PULSEAUDIO_SYM(pa_context_new_with_proplist);
SDL_PULSEAUDIO_SYM(pa_context_set_state_callback);
SDL_PULSEAUDIO_SYM(pa_context_connect);
SDL_PULSEAUDIO_SYM(pa_context_get_sink_info_list);
@ -238,6 +242,9 @@ static int load_pulseaudio_syms(void)
SDL_PULSEAUDIO_SYM(pa_stream_set_write_callback);
SDL_PULSEAUDIO_SYM(pa_stream_set_read_callback);
SDL_PULSEAUDIO_SYM(pa_context_get_server_info);
SDL_PULSEAUDIO_SYM(pa_proplist_new);
SDL_PULSEAUDIO_SYM(pa_proplist_free);
SDL_PULSEAUDIO_SYM(pa_proplist_sets);
// optional
#ifdef SDL_AUDIO_DRIVER_PULSEAUDIO_DYNAMIC
@ -337,6 +344,8 @@ static void PulseContextStateChangeCallback(pa_context *context, void *userdata)
static int ConnectToPulseServer(void)
{
pa_mainloop_api *mainloop_api = NULL;
pa_proplist *proplist = NULL;
const char *icon_name;
int state = 0;
SDL_assert(pulseaudio_threaded_mainloop == NULL);
@ -362,11 +371,22 @@ static int ConnectToPulseServer(void)
mainloop_api = PULSEAUDIO_pa_threaded_mainloop_get_api(pulseaudio_threaded_mainloop);
SDL_assert(mainloop_api != NULL); // this never fails, right?
pulseaudio_context = PULSEAUDIO_pa_context_new(mainloop_api, getAppName());
if (!(proplist = PULSEAUDIO_pa_proplist_new())) {
return SDL_SetError("pa_proplist_new() failed");
}
icon_name = SDL_GetHint(SDL_HINT_AUDIO_DEVICE_APP_ICON_NAME);
if (!icon_name || *icon_name == '\0') {
icon_name = "applications-games";
}
PULSEAUDIO_pa_proplist_sets(proplist, PA_PROP_APPLICATION_ICON_NAME, icon_name);
pulseaudio_context = PULSEAUDIO_pa_context_new_with_proplist(mainloop_api, getAppName(), proplist);
if (!pulseaudio_context) {
SDL_SetError("pa_context_new() failed");
SDL_SetError("pa_context_new_with_proplist() failed");
goto failed;
}
PULSEAUDIO_pa_proplist_free(proplist);
PULSEAUDIO_pa_context_set_state_callback(pulseaudio_context, PulseContextStateChangeCallback, NULL);