[pulseaudio-commits] 3 commits - src/modules src/pulsecore

Arun Raghavan arun at kemper.freedesktop.org
Thu May 5 02:46:49 UTC 2016


 src/modules/module-card-restore.c             |   68 ++++++++
 src/modules/module-switch-on-port-available.c |  209 +++++++++++++++++++++++++-
 src/pulsecore/card.c                          |   46 +++++
 src/pulsecore/card.h                          |   12 +
 src/pulsecore/core.h                          |    1 
 5 files changed, 328 insertions(+), 8 deletions(-)

New commits:
commit 23c15c3b52a958887c1f8cad3c94879a8770ef0e
Author: Tanu Kaskinen <tanuk at iki.fi>
Date:   Fri Mar 4 15:23:32 2016 +0200

    switch-on-port-available: prefer ports that have been selected by the user
    
    Let's assume that there are two output ports, and they are on
    different profiles:
    
        * Integrated speakers (priority: 10000, available)
        * HDMI                (priority:  5900, not available)
    
    Then the user plugs in an HDMI monitor with speakers. Since the HDMI
    priority is lower than the speaker priority, we don't route to HDMI by
    default. However, the user manually switches the profile to use the
    HDMI output.
    
    Then the user plugs out the monitor, so we switch back to speakers.
    When the monitor is plugged back in, the user needs to manually switch
    the audio output again. That should be improved: if the user preferred
    to the HDMI output over the speakers, we should remember that and
    automatically switch to HDMI whenever it becomes available.
    
    The lack of automatic switching is even worse when the monitor goes to
    a sleep mode after some period of inactivity. The monitor audio may
    become unavailable, and PulseAudio can't distinguish that from the
    case where the monitor is physically unplugged. Even worse, the
    monitor may become unavailable for a short while when adjusting the
    display parameters (for example, media center software may adjust the
    display parameters to match the media that is being played back). In
    these cases we clearly should switch automatically back to HDMI when
    it becomes available again.
    
    This patch fixes the problem by setting pa_card.preferred_input_port
    and pa_card.preferred_output_port when the user changes the card
    profile or a port, and switching to the preferred port when it becomes
    available.
    
    BugLink: https://bugs.freedesktop.org/show_bug.cgi?id=93946

diff --git a/src/modules/module-switch-on-port-available.c b/src/modules/module-switch-on-port-available.c
index 2453644..b9a0f3b 100644
--- a/src/modules/module-switch-on-port-available.c
+++ b/src/modules/module-switch-on-port-available.c
@@ -29,7 +29,37 @@
 
 #include "module-switch-on-port-available-symdef.h"
 
-static bool profile_good_for_output(pa_card_profile *profile, unsigned prio) {
+struct card_info {
+    struct userdata *userdata;
+    pa_card *card;
+
+    /* We need to cache the active profile, because we want to compare the old
+     * and new profiles in the PROFILE_CHANGED hook. Without this we'd only
+     * have access to the new profile. */
+    pa_card_profile *active_profile;
+};
+
+struct userdata {
+    pa_hashmap *card_infos; /* pa_card -> struct card_info */
+};
+
+static void card_info_new(struct userdata *u, pa_card *card) {
+    struct card_info *info;
+
+    info = pa_xnew0(struct card_info, 1);
+    info->userdata = u;
+    info->card = card;
+    info->active_profile = card->active_profile;
+
+    pa_hashmap_put(u->card_infos, card, info);
+}
+
+static void card_info_free(struct card_info *info) {
+    pa_hashmap_remove(info->userdata->card_infos, info->card);
+    pa_xfree(info);
+}
+
+static bool profile_good_for_output(pa_card_profile *profile, pa_device_port *port) {
     pa_card *card;
     pa_sink *sink;
     uint32_t idx;
@@ -47,19 +77,21 @@ static bool profile_good_for_output(pa_card_profile *profile, unsigned prio) {
     if (card->active_profile->max_source_channels != profile->max_source_channels)
         return false;
 
-    /* Try not to switch to HDMI sinks from analog when HDMI is becoming available */
+    if (port == card->preferred_output_port)
+        return true;
+
     PA_IDXSET_FOREACH(sink, card->sinks, idx) {
         if (!sink->active_port)
             continue;
 
-        if ((sink->active_port->available != PA_AVAILABLE_NO) && (sink->active_port->priority >= prio))
+        if ((sink->active_port->available != PA_AVAILABLE_NO) && (sink->active_port->priority >= port->priority))
             return false;
     }
 
     return true;
 }
 
-static bool profile_good_for_input(pa_card_profile *profile, unsigned prio) {
+static bool profile_good_for_input(pa_card_profile *profile, pa_device_port *port) {
     pa_card *card;
     pa_source *source;
     uint32_t idx;
@@ -77,11 +109,14 @@ static bool profile_good_for_input(pa_card_profile *profile, unsigned prio) {
     if (card->active_profile->max_sink_channels != profile->max_sink_channels)
         return false;
 
+    if (port == card->preferred_input_port)
+        return true;
+
     PA_IDXSET_FOREACH(source, card->sources, idx) {
         if (!source->active_port)
             continue;
 
-        if ((source->active_port->available != PA_AVAILABLE_NO) && (source->active_port->priority >= prio))
+        if ((source->active_port->available != PA_AVAILABLE_NO) && (source->active_port->priority >= port->priority))
             return false;
     }
 
@@ -105,12 +140,12 @@ static int try_to_switch_profile(pa_device_port *port) {
         switch (port->direction) {
             case PA_DIRECTION_OUTPUT:
                 name = profile->output_name;
-                good = profile_good_for_output(profile, port->priority);
+                good = profile_good_for_output(profile, port);
                 break;
 
             case PA_DIRECTION_INPUT:
                 name = profile->input_name;
-                good = profile_good_for_input(profile, port->priority);
+                good = profile_good_for_input(profile, port);
                 break;
         }
 
@@ -324,9 +359,142 @@ static pa_hook_result_t source_new_hook_callback(pa_core *c, pa_source_new_data
     return PA_HOOK_OK;
 }
 
+static pa_hook_result_t card_put_hook_callback(pa_core *core, pa_card *card, struct userdata *u) {
+    card_info_new(u, card);
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t card_unlink_hook_callback(pa_core *core, pa_card *card, struct userdata *u) {
+    card_info_free(pa_hashmap_get(u->card_infos, card));
+
+    return PA_HOOK_OK;
+}
+
+static void update_preferred_input_port(pa_card *card, pa_card_profile *old_profile, pa_card_profile *new_profile) {
+    pa_source *source;
+
+    /* If the profile change didn't affect input, it doesn't indicate change in
+     * the user's input port preference. */
+    if (pa_safe_streq(old_profile->input_name, new_profile->input_name))
+        return;
+
+    /* If there are more than one source, we don't know which of those the user
+     * prefers. If there are no sources, then the user doesn't seem to care
+     * about input at all. */
+    if (pa_idxset_size(card->sources) != 1) {
+        pa_card_set_preferred_port(card, PA_DIRECTION_INPUT, NULL);
+        return;
+    }
+
+    /* If the profile change modified the set of sinks, then it's unclear
+     * whether the user wanted to activate some specific input port, or was the
+     * input change only a side effect of activating some output. If the new
+     * profile contains no sinks, though, then we know the user only cares
+     * about input. */
+    if (pa_idxset_size(card->sinks) > 0 && !pa_safe_streq(old_profile->output_name, new_profile->output_name)) {
+        pa_card_set_preferred_port(card, PA_DIRECTION_INPUT, NULL);
+        return;
+    }
+
+    source = pa_idxset_first(card->sources, NULL);
+
+    /* We know the user wanted to activate this source. The user might not have
+     * wanted to activate the port that was selected by default, but if that's
+     * the case, the user will change the port manually, and we'll update the
+     * port preference at that time. If no port change occurs, we can assume
+     * that the user likes the port that is now active. */
+    pa_card_set_preferred_port(card, PA_DIRECTION_INPUT, source->active_port);
+}
+
+static void update_preferred_output_port(pa_card *card, pa_card_profile *old_profile, pa_card_profile *new_profile) {
+    pa_sink *sink;
+
+    /* If the profile change didn't affect output, it doesn't indicate change in
+     * the user's output port preference. */
+    if (pa_safe_streq(old_profile->output_name, new_profile->output_name))
+        return;
+
+    /* If there are more than one sink, we don't know which of those the user
+     * prefers. If there are no sinks, then the user doesn't seem to care about
+     * output at all. */
+    if (pa_idxset_size(card->sinks) != 1) {
+        pa_card_set_preferred_port(card, PA_DIRECTION_OUTPUT, NULL);
+        return;
+    }
+
+    /* If the profile change modified the set of sources, then it's unclear
+     * whether the user wanted to activate some specific output port, or was
+     * the output change only a side effect of activating some input. If the
+     * new profile contains no sources, though, then we know the user only
+     * cares about output. */
+    if (pa_idxset_size(card->sources) > 0 && !pa_safe_streq(old_profile->input_name, new_profile->input_name)) {
+        pa_card_set_preferred_port(card, PA_DIRECTION_OUTPUT, NULL);
+        return;
+    }
+
+    sink = pa_idxset_first(card->sinks, NULL);
+
+    /* We know the user wanted to activate this sink. The user might not have
+     * wanted to activate the port that was selected by default, but if that's
+     * the case, the user will change the port manually, and we'll update the
+     * port preference at that time. If no port change occurs, we can assume
+     * that the user likes the port that is now active. */
+    pa_card_set_preferred_port(card, PA_DIRECTION_OUTPUT, sink->active_port);
+}
+
+static pa_hook_result_t card_profile_changed_callback(pa_core *core, pa_card *card, struct userdata *u) {
+    struct card_info *info;
+    pa_card_profile *old_profile;
+    pa_card_profile *new_profile;
+
+    info = pa_hashmap_get(u->card_infos, card);
+    old_profile = info->active_profile;
+    new_profile = card->active_profile;
+    info->active_profile = new_profile;
+
+    /* This profile change wasn't initiated by the user, so it doesn't signal
+     * a change in the user's port preferences. */
+    if (!card->save_profile)
+        return PA_HOOK_OK;
+
+    update_preferred_input_port(card, old_profile, new_profile);
+    update_preferred_output_port(card, old_profile, new_profile);
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t source_port_changed_callback(pa_core *core, pa_source *source, void *userdata) {
+    if (!source->save_port)
+        return PA_HOOK_OK;
+
+    pa_card_set_preferred_port(source->card, PA_DIRECTION_INPUT, source->active_port);
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t sink_port_changed_callback(pa_core *core, pa_sink *sink, void *userdata) {
+    if (!sink->save_port)
+        return PA_HOOK_OK;
+
+    pa_card_set_preferred_port(sink->card, PA_DIRECTION_OUTPUT, sink->active_port);
+
+    return PA_HOOK_OK;
+}
+
 int pa__init(pa_module*m) {
+    struct userdata *u;
+    pa_card *card;
+    uint32_t idx;
+
     pa_assert(m);
 
+    u = m->userdata = pa_xnew0(struct userdata, 1);
+    u->card_infos = pa_hashmap_new(NULL, NULL);
+
+    PA_IDXSET_FOREACH(card, m->core->cards, idx)
+        card_info_new(u, card);
+
     /* Make sure we are after module-device-restore, so we can overwrite that suggestion if necessary */
     pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SINK_NEW],
                            PA_HOOK_NORMAL, (pa_hook_cb_t) sink_new_hook_callback, NULL);
@@ -334,8 +502,35 @@ int pa__init(pa_module*m) {
                            PA_HOOK_NORMAL, (pa_hook_cb_t) source_new_hook_callback, NULL);
     pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_PORT_AVAILABLE_CHANGED],
                            PA_HOOK_LATE, (pa_hook_cb_t) port_available_hook_callback, NULL);
+    pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_CARD_PUT],
+                           PA_HOOK_NORMAL, (pa_hook_cb_t) card_put_hook_callback, u);
+    pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_CARD_UNLINK],
+                           PA_HOOK_NORMAL, (pa_hook_cb_t) card_unlink_hook_callback, u);
+    pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_CARD_PROFILE_CHANGED],
+                           PA_HOOK_NORMAL, (pa_hook_cb_t) card_profile_changed_callback, u);
+    pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SOURCE_PORT_CHANGED],
+                           PA_HOOK_NORMAL, (pa_hook_cb_t) source_port_changed_callback, NULL);
+    pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_SINK_PORT_CHANGED],
+                           PA_HOOK_NORMAL, (pa_hook_cb_t) sink_port_changed_callback, NULL);
 
     handle_all_unavailable(m->core);
 
     return 0;
 }
+
+void pa__done(pa_module *module) {
+    struct userdata *u;
+    struct card_info *info;
+
+    pa_assert(module);
+
+    if (!(u = module->userdata))
+        return;
+
+    while ((info = pa_hashmap_last(u->card_infos)))
+        card_info_free(info);
+
+    pa_hashmap_free(u->card_infos);
+
+    pa_xfree(u);
+}

commit c4058b8d00a51d8c600d950c8ec8967ab9c6f7ea
Author: Tanu Kaskinen <tanuk at iki.fi>
Date:   Fri Mar 4 15:23:31 2016 +0200

    card-restore: restore preferred ports

diff --git a/src/modules/module-card-restore.c b/src/modules/module-card-restore.c
index 2660a2b..7545aa5 100644
--- a/src/modules/module-card-restore.c
+++ b/src/modules/module-card-restore.c
@@ -64,7 +64,7 @@ struct userdata {
     pa_database *database;
 };
 
-#define ENTRY_VERSION 3
+#define ENTRY_VERSION 4
 
 struct port_info {
     char *name;
@@ -75,6 +75,8 @@ struct port_info {
 struct entry {
     char *profile;
     pa_hashmap *ports; /* Port name -> struct port_info */
+    char *preferred_input_port;
+    char *preferred_output_port;
 };
 
 static void save_time_callback(pa_mainloop_api*a, pa_time_event* e, const struct timeval *t, void *userdata) {
@@ -131,6 +133,8 @@ static struct port_info *port_info_new(pa_device_port *port) {
 static void entry_free(struct entry* e) {
     pa_assert(e);
 
+    pa_xfree(e->preferred_output_port);
+    pa_xfree(e->preferred_input_port);
     pa_xfree(e->profile);
     pa_hashmap_free(e->ports);
 
@@ -176,6 +180,12 @@ static bool entrys_equal(struct entry *a, struct entry *b) {
             return false;
     }
 
+    if (!pa_safe_streq(a->preferred_input_port, b->preferred_input_port))
+        return false;
+
+    if (!pa_safe_streq(a->preferred_output_port, b->preferred_output_port))
+        return false;
+
     return true;
 }
 
@@ -201,6 +211,9 @@ static bool entry_write(struct userdata *u, const char *name, const struct entry
         pa_tagstruct_puts(t, p_info->profile);
     }
 
+    pa_tagstruct_puts(t, e->preferred_input_port);
+    pa_tagstruct_puts(t, e->preferred_output_port);
+
     key.data = (char *) name;
     key.size = strlen(name);
 
@@ -314,6 +327,18 @@ static struct entry* entry_read(struct userdata *u, const char *name) {
         }
     }
 
+    if (version >= 4) {
+        const char *preferred_input_port;
+        const char *preferred_output_port;
+
+        if (pa_tagstruct_gets(t, &preferred_input_port) < 0
+                || pa_tagstruct_gets(t, &preferred_output_port) < 0)
+            goto fail;
+
+        e->preferred_input_port = pa_xstrdup(preferred_input_port);
+        e->preferred_output_port = pa_xstrdup(preferred_output_port);
+    }
+
     if (!pa_tagstruct_eof(t))
         goto fail;
 
@@ -547,6 +572,46 @@ static pa_hook_result_t card_new_hook_callback(pa_core *c, pa_card_new_data *new
             if (!p->preferred_profile && p_info->profile)
                 pa_device_port_set_preferred_profile(p, p_info->profile);
         }
+
+    if (e->preferred_input_port) {
+        p = pa_hashmap_get(new_data->ports, e->preferred_input_port);
+        if (p)
+            pa_card_new_data_set_preferred_port(new_data, PA_DIRECTION_INPUT, p);
+    }
+
+    if (e->preferred_output_port) {
+        p = pa_hashmap_get(new_data->ports, e->preferred_output_port);
+        if (p)
+            pa_card_new_data_set_preferred_port(new_data, PA_DIRECTION_OUTPUT, p);
+    }
+
+    entry_free(e);
+
+    return PA_HOOK_OK;
+}
+
+static pa_hook_result_t card_preferred_port_changed_callback(pa_core *core, pa_card_preferred_port_changed_hook_data *data,
+                                                             struct userdata *u) {
+    struct entry *e;
+    pa_card *card;
+
+    card = data->card;
+
+    e = entry_read(u, card->name);
+    if (!e)
+        e = entry_from_card(card);
+
+    if (data->direction == PA_DIRECTION_INPUT) {
+        pa_xfree(e->preferred_input_port);
+        e->preferred_input_port = pa_xstrdup(card->preferred_input_port ? card->preferred_input_port->name : NULL);
+    } else {
+        pa_xfree(e->preferred_output_port);
+        e->preferred_output_port = pa_xstrdup(card->preferred_output_port ? card->preferred_output_port->name : NULL);
+    }
+
+    if (entry_write(u, card->name, e))
+        trigger_save(u);
+
     entry_free(e);
 
     return PA_HOOK_OK;
@@ -570,6 +635,7 @@ int pa__init(pa_module*m) {
 
     pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_CARD_NEW], PA_HOOK_EARLY, (pa_hook_cb_t) card_new_hook_callback, u);
     pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_CARD_PUT], PA_HOOK_NORMAL, (pa_hook_cb_t) card_put_hook_callback, u);
+    pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_CARD_PREFERRED_PORT_CHANGED], PA_HOOK_NORMAL, (pa_hook_cb_t) card_preferred_port_changed_callback, u);
     pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_CARD_PROFILE_CHANGED], PA_HOOK_NORMAL, (pa_hook_cb_t) card_profile_changed_callback, u);
     pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_CARD_PROFILE_ADDED], PA_HOOK_NORMAL, (pa_hook_cb_t) card_profile_added_callback, u);
     pa_module_hook_connect(m, &m->core->hooks[PA_CORE_HOOK_CARD_PROFILE_AVAILABLE_CHANGED],

commit 04040c522f5f62dda50ac927e92453381d419f09
Author: Tanu Kaskinen <tanuk at iki.fi>
Date:   Fri Mar 4 15:23:30 2016 +0200

    card: add preferred_{input, output}_port
    
    I will modify module-switch-on-port-available so that it will keep
    track of which input and output port the user prefers on the card,
    based on the user's profile and port switches. The preference needs
    to be saved on disk, for which I will use module-card-restore.
    
    To facilitate communication between the two modules, this patch adds
    preferred_input_port and preferred_output_port fields to pa_card, and
    a hook for monitoring the variable changes. It would be nice if the
    two modules would communicate directly with each other, but
    implementing that would be somewhat complicated, so I chose this time
    for adding the functionality to the core. In theory some other routing
    module might want to manage the new variables instead of
    module-switch-on-port-available, but admittedly that's not very likely
    to happen...

diff --git a/src/pulsecore/card.c b/src/pulsecore/card.c
index b6cbbf7..0eaccff 100644
--- a/src/pulsecore/card.c
+++ b/src/pulsecore/card.c
@@ -103,6 +103,15 @@ void pa_card_new_data_set_profile(pa_card_new_data *data, const char *profile) {
     data->active_profile = pa_xstrdup(profile);
 }
 
+void pa_card_new_data_set_preferred_port(pa_card_new_data *data, pa_direction_t direction, pa_device_port *port) {
+    pa_assert(data);
+
+    if (direction == PA_DIRECTION_INPUT)
+        data->preferred_input_port = port;
+    else
+        data->preferred_output_port = port;
+}
+
 void pa_card_new_data_done(pa_card_new_data *data) {
 
     pa_assert(data);
@@ -169,6 +178,9 @@ pa_card *pa_card_new(pa_core *core, pa_card_new_data *data) {
     PA_HASHMAP_FOREACH(port, c->ports, state)
         port->card = c;
 
+    c->preferred_input_port = data->preferred_input_port;
+    c->preferred_output_port = data->preferred_output_port;
+
     if (data->active_profile)
         if ((c->active_profile = pa_hashmap_get(c->profiles, data->active_profile)))
             c->save_profile = data->save_profile;
@@ -309,6 +321,40 @@ int pa_card_set_profile(pa_card *c, pa_card_profile *profile, bool save) {
     return 0;
 }
 
+void pa_card_set_preferred_port(pa_card *c, pa_direction_t direction, pa_device_port *port) {
+    pa_device_port *old_port;
+    const char *old_port_str;
+    const char *new_port_str;
+    pa_card_preferred_port_changed_hook_data data;
+
+    pa_assert(c);
+
+    if (direction == PA_DIRECTION_INPUT) {
+        old_port = c->preferred_input_port;
+        old_port_str = c->preferred_input_port ? c->preferred_input_port->name : "(unset)";
+    } else {
+        old_port = c->preferred_output_port;
+        old_port_str = c->preferred_output_port ? c->preferred_output_port->name : "(unset)";
+    }
+
+    if (port == old_port)
+        return;
+
+    new_port_str = port ? port->name : "(unset)";
+
+    if (direction == PA_DIRECTION_INPUT) {
+        c->preferred_input_port = port;
+        pa_log_debug("%s: preferred_input_port: %s -> %s", c->name, old_port_str, new_port_str);
+    } else {
+        c->preferred_output_port = port;
+        pa_log_debug("%s: preferred_output_port: %s -> %s", c->name, old_port_str, new_port_str);
+    }
+
+    data.card = c;
+    data.direction = direction;
+    pa_hook_fire(&c->core->hooks[PA_CORE_HOOK_CARD_PREFERRED_PORT_CHANGED], &data);
+}
+
 int pa_card_suspend(pa_card *c, bool suspend, pa_suspend_cause_t cause) {
     pa_sink *sink;
     pa_source *source;
diff --git a/src/pulsecore/card.h b/src/pulsecore/card.h
index 30bfc0e..79966f3 100644
--- a/src/pulsecore/card.h
+++ b/src/pulsecore/card.h
@@ -79,6 +79,8 @@ struct pa_card {
     pa_card_profile *active_profile;
 
     pa_hashmap *ports;
+    pa_device_port *preferred_input_port;
+    pa_device_port *preferred_output_port;
 
     bool save_profile:1;
 
@@ -98,12 +100,19 @@ typedef struct pa_card_new_data {
     char *active_profile;
 
     pa_hashmap *ports;
+    pa_device_port *preferred_input_port;
+    pa_device_port *preferred_output_port;
 
     bool namereg_fail:1;
 
     bool save_profile:1;
 } pa_card_new_data;
 
+typedef struct {
+    pa_card *card;
+    pa_direction_t direction;
+} pa_card_preferred_port_changed_hook_data;
+
 pa_card_profile *pa_card_profile_new(const char *name, const char *description, size_t extra);
 void pa_card_profile_free(pa_card_profile *c);
 
@@ -113,6 +122,7 @@ void pa_card_profile_set_available(pa_card_profile *c, pa_available_t available)
 pa_card_new_data *pa_card_new_data_init(pa_card_new_data *data);
 void pa_card_new_data_set_name(pa_card_new_data *data, const char *name);
 void pa_card_new_data_set_profile(pa_card_new_data *data, const char *profile);
+void pa_card_new_data_set_preferred_port(pa_card_new_data *data, pa_direction_t direction, pa_device_port *port);
 void pa_card_new_data_done(pa_card_new_data *data);
 
 pa_card *pa_card_new(pa_core *c, pa_card_new_data *data);
@@ -122,6 +132,8 @@ void pa_card_add_profile(pa_card *c, pa_card_profile *profile);
 
 int pa_card_set_profile(pa_card *c, pa_card_profile *profile, bool save);
 
+void pa_card_set_preferred_port(pa_card *c, pa_direction_t direction, pa_device_port *port);
+
 int pa_card_suspend(pa_card *c, bool suspend, pa_suspend_cause_t cause);
 
 #endif
diff --git a/src/pulsecore/core.h b/src/pulsecore/core.h
index 1a3c490..69ab4d0 100644
--- a/src/pulsecore/core.h
+++ b/src/pulsecore/core.h
@@ -120,6 +120,7 @@ typedef enum pa_core_hook {
     PA_CORE_HOOK_CARD_NEW,
     PA_CORE_HOOK_CARD_PUT,
     PA_CORE_HOOK_CARD_UNLINK,
+    PA_CORE_HOOK_CARD_PREFERRED_PORT_CHANGED,
     PA_CORE_HOOK_CARD_PROFILE_CHANGED,
     PA_CORE_HOOK_CARD_PROFILE_ADDED,
     PA_CORE_HOOK_CARD_PROFILE_AVAILABLE_CHANGED,



More information about the pulseaudio-commits mailing list