diff --git a/gvc-mixer-control.c b/gvc-mixer-control.c index a43ae1d..2dc2cb7 100644 --- a/gvc-mixer-control.c +++ b/gvc-mixer-control.c @@ -62,6 +62,7 @@ struct GvcMixerControlPrivate pa_glib_mainloop *pa_mainloop; pa_mainloop_api *pa_api; pa_context *pa_context; + guint server_protocol_version; int n_outstanding; guint reconnect_id; char *name; @@ -104,6 +105,11 @@ struct GvcMixerControlPrivate gboolean has_headsetmic; gboolean has_headphonemic; gboolean headset_plugged_in; + char *headphones_name; + char *headsetmic_name; + char *headphonemic_name; + char *internalspk_name; + char *internalmic_name; #endif /* HAVE_ALSA */ GvcMixerControlState state; @@ -2173,6 +2179,19 @@ source_info_cb (pa_context *c, s = data->port_name_to_set; + for (j = 0; j < i->n_ports; j++) { + if (g_str_equal (i->ports[j]->name, s)) { + o = pa_context_set_default_source (c, + i->name, + NULL, + NULL); + if (o == NULL) { + g_warning ("pa_context_set_default_source() failed"); + return; + } + } + } + if (i->active_port && strcmp (i->active_port->name, s) == 0) return; @@ -2196,6 +2215,9 @@ gvc_mixer_control_set_port_status_for_headset (GvcMixerControl *control, pa_operation *o; PortStatusData *data; + if (port_name == NULL) + return; + data = g_new0 (PortStatusData, 1); data->port_name_to_set = g_strdup (port_name); data->headset_card = id; @@ -2209,6 +2231,18 @@ gvc_mixer_control_set_port_status_for_headset (GvcMixerControl *control, } #endif /* HAVE_ALSA */ +static void +free_priv_port_names (GvcMixerControl *control) +{ +#ifdef HAVE_ALSA + g_clear_pointer (&control->priv->headphones_name, g_free); + g_clear_pointer (&control->priv->headsetmic_name, g_free); + g_clear_pointer (&control->priv->headphonemic_name, g_free); + g_clear_pointer (&control->priv->internalspk_name, g_free); + g_clear_pointer (&control->priv->internalmic_name, g_free); +#endif +} + void gvc_mixer_control_set_headset_port (GvcMixerControl *control, guint id, @@ -2219,16 +2253,16 @@ gvc_mixer_control_set_headset_port (GvcMixerControl *control, #ifdef HAVE_ALSA switch (choice) { case GVC_HEADSET_PORT_CHOICE_HEADPHONES: - gvc_mixer_control_set_port_status_for_headset (control, id, "analog-output-headphones", TRUE); - gvc_mixer_control_set_port_status_for_headset (control, id, "analog-input-internal-mic", FALSE); + gvc_mixer_control_set_port_status_for_headset (control, id, control->priv->headphones_name, TRUE); + gvc_mixer_control_set_port_status_for_headset (control, id, control->priv->internalmic_name, FALSE); break; case GVC_HEADSET_PORT_CHOICE_HEADSET: - gvc_mixer_control_set_port_status_for_headset (control, id, "analog-output-headphones", TRUE); - gvc_mixer_control_set_port_status_for_headset (control, id, "analog-input-headset-mic", FALSE); + gvc_mixer_control_set_port_status_for_headset (control, id, control->priv->headphones_name, TRUE); + gvc_mixer_control_set_port_status_for_headset (control, id, control->priv->headsetmic_name, FALSE); break; case GVC_HEADSET_PORT_CHOICE_MIC: - gvc_mixer_control_set_port_status_for_headset (control, id, "analog-output-speaker", TRUE); - gvc_mixer_control_set_port_status_for_headset (control, id, "analog-input-headphone-mic", FALSE); + gvc_mixer_control_set_port_status_for_headset (control, id, control->priv->internalspk_name, TRUE); + gvc_mixer_control_set_port_status_for_headset (control, id, control->priv->headphonemic_name, FALSE); break; case GVC_HEADSET_PORT_CHOICE_NONE: default: @@ -2244,16 +2278,24 @@ typedef struct { const pa_card_port_info *headphones; const pa_card_port_info *headsetmic; const pa_card_port_info *headphonemic; + const pa_card_port_info *internalmic; + const pa_card_port_info *internalspk; } headset_ports; /* TODO: Check if we still need this with the changed PA port names - In PulseAudio ports will show up with the following names: + In PulseAudio without ucm, ports will show up with the following names: Headphones - analog-output-headphones Headset mic - analog-input-headset-mic (was: analog-input-microphone-headset) Jack in mic-in mode - analog-input-headphone-mic (was: analog-input-microphone) + In PulseAudio with ucm, the ports name depends on the ucm, with the current + ucm2, the ports will show up with the following names: + Headphones - [Out] Headphone + Headset mic - [In] Headset + Jack in mic-in mode - [In] Mic2 + However, since regular mics also show up as analog-input-microphone, we need to check for certain controls on alsa mixer level too, to know if we deal with a separate mic jack, or a multi-function jack with a @@ -2268,10 +2310,18 @@ typedef struct { Headset Mic Jack - indicates headset jack where hardware can distinguish between headphones and headsets. There is no use popping up a dialog in this case, unless we already need to do this for the mic-in mode. + + From the PA_PROCOTOL_VERSION=34, The device_port structure adds 2 members + available_group and type, with the help of these 2 members, we could + consolidate the port checking and port setting for non-ucm and with-ucm + cases. */ +#define GET_PORT_NAME(x) (x ? g_strdup (x->name) : NULL) + static headset_ports * -get_headset_ports (const pa_card_info *c) +get_headset_ports (GvcMixerControl *control, + const pa_card_info *c) { headset_ports *h; guint i; @@ -2280,13 +2330,42 @@ get_headset_ports (const pa_card_info *c) for (i = 0; i < c->n_ports; i++) { pa_card_port_info *p = c->ports[i]; - - if (strcmp (p->name, "analog-output-headphones") == 0) - h->headphones = p; - else if (strcmp (p->name, "analog-input-headset-mic") == 0) - h->headsetmic = p; - else if (strcmp(p->name, "analog-input-headphone-mic") == 0) - h->headphonemic = p; + if (control->priv->server_protocol_version < 34) { + if (g_str_equal (p->name, "analog-output-headphones")) + h->headphones = p; + else if (g_str_equal (p->name, "analog-input-headset-mic")) + h->headsetmic = p; + else if (g_str_equal (p->name, "analog-input-headphone-mic")) + h->headphonemic = p; + else if (g_str_equal (p->name, "analog-input-internal-mic")) + h->internalmic = p; + else if (g_str_equal (p->name, "analog-output-speaker")) + h->internalspk = p; + } else { +#if (PA_PROTOCOL_VERSION >= 34) + if (p->available_group && strcmp (p->available_group, "Headphone Mic") == 0) { + if (p->type == PA_DEVICE_PORT_TYPE_HEADPHONES) + h->headphones = p; + else if (p->type == PA_DEVICE_PORT_TYPE_HEADSET) + h->headsetmic = p; + else if (p->type == PA_DEVICE_PORT_TYPE_MIC) + h->headphonemic = p; + } else if (p->type == PA_DEVICE_PORT_TYPE_SPEAKER) { + if (strcmp (p->name, "analog-output-speaker") == 0 || + strcmp (p->name, "[Out] Speaker") == 0 ) + h->internalspk = p; + } else if (p->type == PA_DEVICE_PORT_TYPE_MIC) { + if (strcmp (p->name, "analog-input-internal-mic") == 0 || + strcmp (p->name, "[In] Mic1") == 0 ) + h->internalmic = p; + } +#else + g_warning_once ("libgnome-volume-control running against PulseAudio %u, " + "but compiled against older %d, report a bug to your distribution", + control->priv->server_protocol_version, + PA_PROTOCOL_VERSION); +#endif + } } return h; } @@ -2353,7 +2432,7 @@ check_audio_device_selection_needed (GvcMixerControl *control, start_dialog = FALSE; stop_dialog = FALSE; - h = get_headset_ports (info); + h = get_headset_ports (control, info); if (!h->headphones || (!h->headsetmic && !h->headphonemic)) { @@ -2363,7 +2442,8 @@ check_audio_device_selection_needed (GvcMixerControl *control, if (control->priv->headset_card != (int) info->index) { int cardindex; - gboolean hsmic, hpmic; + gboolean hsmic = TRUE; + gboolean hpmic = TRUE; const char *s; s = pa_proplist_gets (info->proplist, "alsa.card"); @@ -2374,8 +2454,10 @@ check_audio_device_selection_needed (GvcMixerControl *control, if (cardindex == 0 && strcmp(s, "0") != 0) goto out; - if (!verify_alsa_card(cardindex, &hsmic, &hpmic)) - goto out; + if (control->priv->server_protocol_version < 34) { + if (!verify_alsa_card(cardindex, &hsmic, &hpmic)) + goto out; + } control->priv->headset_card = info->index; control->priv->has_headsetmic = hsmic && h->headsetmic; @@ -2386,6 +2468,12 @@ check_audio_device_selection_needed (GvcMixerControl *control, } control->priv->headset_plugged_in = h->headphones->available != PA_PORT_AVAILABLE_NO; + free_priv_port_names (control); + control->priv->headphones_name = GET_PORT_NAME(h->headphones); + control->priv->headsetmic_name = GET_PORT_NAME(h->headsetmic); + control->priv->headphonemic_name = GET_PORT_NAME(h->headphonemic); + control->priv->internalspk_name = GET_PORT_NAME(h->internalspk); + control->priv->internalmic_name = GET_PORT_NAME(h->internalmic); if (!start_dialog && !stop_dialog) @@ -3268,6 +3356,7 @@ gvc_mixer_control_ready (GvcMixerControl *control) req_update_sink_input_info (control, -1); req_update_source_output_info (control, -1); + control->priv->server_protocol_version = pa_context_get_server_protocol_version (control->priv->pa_context); control->priv->n_outstanding = 6; @@ -3350,6 +3439,7 @@ idle_reconnect (gpointer data) if (control->priv->pa_context) { pa_context_unref (control->priv->pa_context); control->priv->pa_context = NULL; + control->priv->server_protocol_version = 0; gvc_mixer_new_pa_context (control); } @@ -3503,6 +3593,7 @@ gvc_mixer_control_dispose (GObject *object) control->priv->ui_inputs = NULL; } + free_priv_port_names (control); G_OBJECT_CLASS (gvc_mixer_control_parent_class)->dispose (object); }