summaryrefslogtreecommitdiffstats
path: root/src/pulsecore
diff options
context:
space:
mode:
Diffstat (limited to 'src/pulsecore')
-rw-r--r--src/pulsecore/card.c46
-rw-r--r--src/pulsecore/card.h5
-rw-r--r--src/pulsecore/cli-command.c78
-rw-r--r--src/pulsecore/cli-text.c36
-rw-r--r--src/pulsecore/conf-parser.c50
-rw-r--r--src/pulsecore/core-util.c45
-rw-r--r--src/pulsecore/core-util.h10
-rw-r--r--src/pulsecore/database-gdbm.c7
-rw-r--r--src/pulsecore/hashmap.c42
-rw-r--r--src/pulsecore/hashmap.h13
-rw-r--r--src/pulsecore/idxset.c14
-rw-r--r--src/pulsecore/idxset.h3
-rw-r--r--src/pulsecore/native-common.h4
-rw-r--r--src/pulsecore/pdispatch.c4
-rw-r--r--src/pulsecore/protocol-native.c110
-rw-r--r--src/pulsecore/pstream.c8
-rw-r--r--src/pulsecore/sample-util.c2
-rw-r--r--src/pulsecore/sample-util.h59
-rw-r--r--src/pulsecore/sink-input.c10
-rw-r--r--src/pulsecore/sink.c161
-rw-r--r--src/pulsecore/sink.h47
-rw-r--r--src/pulsecore/source.c122
-rw-r--r--src/pulsecore/source.h32
23 files changed, 817 insertions, 91 deletions
diff --git a/src/pulsecore/card.c b/src/pulsecore/card.c
index 59b8cda6..2f0a3af0 100644
--- a/src/pulsecore/card.c
+++ b/src/pulsecore/card.c
@@ -148,15 +148,12 @@ pa_card *pa_card_new(pa_core *core, pa_card_new_data *data) {
c->save_profile = data->save_profile;
if (!c->active_profile && c->profiles) {
- void *state = NULL;
+ void *state;
pa_card_profile *p;
- while ((p = pa_hashmap_iterate(c->profiles, &state, NULL))) {
- if (!c->active_profile ||
- p->priority > c->active_profile->priority)
-
+ PA_HASHMAP_FOREACH(p, c->profiles, state)
+ if (!c->active_profile || p->priority > c->active_profile->priority)
c->active_profile = p;
- }
}
c->userdata = NULL;
@@ -164,6 +161,7 @@ pa_card *pa_card_new(pa_core *core, pa_card_new_data *data) {
pa_device_init_description(c->proplist);
pa_device_init_icon(c->proplist, TRUE);
+ pa_device_init_intended_roles(c->proplist);
pa_assert_se(pa_idxset_put(core->cards, c, &c->index) >= 0);
@@ -176,7 +174,6 @@ pa_card *pa_card_new(pa_core *core, pa_card_new_data *data) {
void pa_card_free(pa_card *c) {
pa_core *core;
- pa_card_profile *profile;
pa_assert(c);
pa_assert(c->core);
@@ -199,8 +196,10 @@ void pa_card_free(pa_card *c) {
pa_idxset_free(c->sources, NULL, NULL);
if (c->profiles) {
- while ((profile = pa_hashmap_steal_first(c->profiles)))
- pa_card_profile_free(profile);
+ pa_card_profile *p;
+
+ while ((p = pa_hashmap_steal_first(c->profiles)))
+ pa_card_profile_free(p);
pa_hashmap_free(c->profiles, NULL, NULL);
}
@@ -213,26 +212,27 @@ void pa_card_free(pa_card *c) {
int pa_card_set_profile(pa_card *c, const char *name, pa_bool_t save) {
pa_card_profile *profile;
+ int r;
pa_assert(c);
if (!c->set_profile) {
- pa_log_warn("set_profile() operation not implemented for card %u \"%s\"", c->index, c->name);
- return -1;
+ pa_log_debug("set_profile() operation not implemented for card %u \"%s\"", c->index, c->name);
+ return -PA_ERR_NOTIMPLEMENTED;
}
if (!c->profiles)
- return -1;
+ return -PA_ERR_NOENTITY;
if (!(profile = pa_hashmap_get(c->profiles, name)))
- return -1;
+ return -PA_ERR_NOENTITY;
if (c->active_profile == profile) {
c->save_profile = c->save_profile || save;
return 0;
}
- if (c->set_profile(c, profile) < 0)
- return -1;
+ if ((r = c->set_profile(c, profile)) < 0)
+ return r;
pa_subscription_post(c->core, PA_SUBSCRIPTION_EVENT_CARD|PA_SUBSCRIPTION_EVENT_CHANGE, c->index);
@@ -253,11 +253,19 @@ int pa_card_suspend(pa_card *c, pa_bool_t suspend, pa_suspend_cause_t cause) {
pa_assert(c);
pa_assert(cause != 0);
- for (sink = pa_idxset_first(c->sinks, &idx); sink; sink = pa_idxset_next(c->sinks, &idx))
- ret -= pa_sink_suspend(sink, suspend, cause) < 0;
+ for (sink = pa_idxset_first(c->sinks, &idx); sink; sink = pa_idxset_next(c->sinks, &idx)) {
+ int r;
- for (source = pa_idxset_first(c->sources, &idx); source; source = pa_idxset_next(c->sources, &idx))
- ret -= pa_source_suspend(source, suspend, cause) < 0;
+ if ((r = pa_sink_suspend(sink, suspend, cause)) < 0)
+ ret = r;
+ }
+
+ for (source = pa_idxset_first(c->sources, &idx); source; source = pa_idxset_next(c->sources, &idx)) {
+ int r;
+
+ if ((r = pa_source_suspend(source, suspend, cause)) < 0)
+ ret = r;
+ }
return ret;
}
diff --git a/src/pulsecore/card.h b/src/pulsecore/card.h
index 415ab678..2d691b67 100644
--- a/src/pulsecore/card.h
+++ b/src/pulsecore/card.h
@@ -63,7 +63,7 @@ struct pa_card {
pa_hashmap *profiles;
pa_card_profile *active_profile;
- pa_bool_t save_profile;
+ pa_bool_t save_profile:1;
void *userdata;
@@ -72,9 +72,8 @@ struct pa_card {
typedef struct pa_card_new_data {
char *name;
- char *description;
-
pa_proplist *proplist;
+
const char *driver;
pa_module *module;
diff --git a/src/pulsecore/cli-command.c b/src/pulsecore/cli-command.c
index 644de96e..e2c3c066 100644
--- a/src/pulsecore/cli-command.c
+++ b/src/pulsecore/cli-command.c
@@ -125,6 +125,8 @@ static int pa_cli_command_update_source_proplist(pa_core *c, pa_tokenizer *t, pa
static int pa_cli_command_update_sink_input_proplist(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
static int pa_cli_command_update_source_output_proplist(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
static int pa_cli_command_card_profile(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_sink_port(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
+static int pa_cli_command_source_port(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail);
/* A method table for all available commands */
@@ -176,10 +178,12 @@ static const struct command commands[] = {
{ "suspend-source", pa_cli_command_suspend_source, "Suspend source (args: index|name, bool)", 3},
{ "suspend", pa_cli_command_suspend, "Suspend all sinks and all sources (args: bool)", 2},
{ "set-card-profile", pa_cli_command_card_profile, "Change the profile of a card (args: index, name)", 3},
+ { "set-sink-port", pa_cli_command_sink_port, "Change the port of a sink (args: index, name)", 3},
+ { "set-source-port", pa_cli_command_source_port, "Change the port of a source (args: index, name)", 3},
{ "set-log-level", pa_cli_command_log_level, "Change the log level (args: numeric level)", 2},
{ "set-log-meta", pa_cli_command_log_meta, "Show source code location in log messages (args: bool)", 2},
{ "set-log-time", pa_cli_command_log_time, "Show timestamps in log messages (args: bool)", 2},
- { "set-log-backtrace", pa_cli_command_log_backtrace, "Show bakctrace in log messages (args: frames)", 2},
+ { "set-log-backtrace", pa_cli_command_log_backtrace, "Show backtrace in log messages (args: frames)", 2},
{ NULL, NULL, NULL, 0 }
};
@@ -526,7 +530,7 @@ static int pa_cli_command_sink_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *bu
}
pa_cvolume_set(&cvolume, sink->sample_spec.channels, volume);
- pa_sink_set_volume(sink, &cvolume, TRUE, TRUE, TRUE);
+ pa_sink_set_volume(sink, &cvolume, TRUE, TRUE, TRUE, TRUE);
return 0;
}
@@ -604,7 +608,7 @@ static int pa_cli_command_source_volume(pa_core *c, pa_tokenizer *t, pa_strbuf *
}
pa_cvolume_set(&cvolume, source->sample_spec.channels, volume);
- pa_source_set_volume(source, &cvolume);
+ pa_source_set_volume(source, &cvolume, TRUE);
return 0;
}
@@ -638,7 +642,7 @@ static int pa_cli_command_sink_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *buf,
return -1;
}
- pa_sink_set_mute(sink, mute);
+ pa_sink_set_mute(sink, mute, TRUE);
return 0;
}
@@ -672,7 +676,7 @@ static int pa_cli_command_source_mute(pa_core *c, pa_tokenizer *t, pa_strbuf *bu
return -1;
}
- pa_source_set_mute(source, mute);
+ pa_source_set_mute(source, mute, TRUE);
return 0;
}
@@ -1476,6 +1480,70 @@ static int pa_cli_command_card_profile(pa_core *c, pa_tokenizer *t, pa_strbuf *b
return 0;
}
+static int pa_cli_command_sink_port(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
+ const char *n, *p;
+ pa_sink *sink;
+
+ pa_core_assert_ref(c);
+ pa_assert(t);
+ pa_assert(buf);
+ pa_assert(fail);
+
+ if (!(n = pa_tokenizer_get(t, 1))) {
+ pa_strbuf_puts(buf, "You need to specify a sink either by its name or its index.\n");
+ return -1;
+ }
+
+ if (!(p = pa_tokenizer_get(t, 2))) {
+ pa_strbuf_puts(buf, "You need to specify a profile by its name.\n");
+ return -1;
+ }
+
+ if (!(sink = pa_namereg_get(c, n, PA_NAMEREG_SINK))) {
+ pa_strbuf_puts(buf, "No sink found by this name or index.\n");
+ return -1;
+ }
+
+ if (pa_sink_set_port(sink, p, TRUE) < 0) {
+ pa_strbuf_printf(buf, "Failed to set sink port to '%s'.\n", p);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int pa_cli_command_source_port(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
+ const char *n, *p;
+ pa_source *source;
+
+ pa_core_assert_ref(c);
+ pa_assert(t);
+ pa_assert(buf);
+ pa_assert(fail);
+
+ if (!(n = pa_tokenizer_get(t, 1))) {
+ pa_strbuf_puts(buf, "You need to specify a source either by its name or its index.\n");
+ return -1;
+ }
+
+ if (!(p = pa_tokenizer_get(t, 2))) {
+ pa_strbuf_puts(buf, "You need to specify a profile by its name.\n");
+ return -1;
+ }
+
+ if (!(source = pa_namereg_get(c, n, PA_NAMEREG_SOURCE))) {
+ pa_strbuf_puts(buf, "No source found by this name or index.\n");
+ return -1;
+ }
+
+ if (pa_source_set_port(source, p, TRUE) < 0) {
+ pa_strbuf_printf(buf, "Failed to set source port to '%s'.\n", p);
+ return -1;
+ }
+
+ return 0;
+}
+
static int pa_cli_command_dump(pa_core *c, pa_tokenizer *t, pa_strbuf *buf, pa_bool_t *fail) {
pa_module *m;
pa_sink *sink;
diff --git a/src/pulsecore/cli-text.c b/src/pulsecore/cli-text.c
index bc863f05..9395513d 100644
--- a/src/pulsecore/cli-text.c
+++ b/src/pulsecore/cli-text.c
@@ -139,11 +139,10 @@ char *pa_card_list_to_string(pa_core *c) {
if (card->profiles) {
pa_card_profile *p;
- void *state = NULL;
+ void *state;
pa_strbuf_puts(s, "\tprofiles:\n");
-
- while ((p = pa_hashmap_iterate(card->profiles, &state, NULL)))
+ PA_HASHMAP_FOREACH(p, card->profiles, state)
pa_strbuf_printf(s, "\t\t%s: %s (priority %u)\n", p->name, p->description, p->priority);
}
@@ -307,6 +306,22 @@ char *pa_sink_list_to_string(pa_core *c) {
t = pa_proplist_to_string_sep(sink->proplist, "\n\t\t");
pa_strbuf_printf(s, "\tproperties:\n\t\t%s\n", t);
pa_xfree(t);
+
+ if (sink->ports) {
+ pa_device_port *p;
+ void *state;
+
+ pa_strbuf_puts(s, "\tports:\n");
+ PA_HASHMAP_FOREACH(p, sink->ports, state)
+ pa_strbuf_printf(s, "\t\t%s: %s (priority %u)\n", p->name, p->description, p->priority);
+ }
+
+
+ if (sink->active_port)
+ pa_strbuf_printf(
+ s,
+ "\tactive port: <%s>\n",
+ sink->active_port->name);
}
return pa_strbuf_tostring_free(s);
@@ -412,6 +427,21 @@ char *pa_source_list_to_string(pa_core *c) {
t = pa_proplist_to_string_sep(source->proplist, "\n\t\t");
pa_strbuf_printf(s, "\tproperties:\n\t\t%s\n", t);
pa_xfree(t);
+
+ if (source->ports) {
+ pa_device_port *p;
+ void *state;
+
+ pa_strbuf_puts(s, "\tports:\n");
+ PA_HASHMAP_FOREACH(p, source->ports, state)
+ pa_strbuf_printf(s, "\t\t%s: %s (priority %u)\n", p->name, p->description, p->priority);
+ }
+
+ if (source->active_port)
+ pa_strbuf_printf(
+ s,
+ "\tactive port: <%s>\n",
+ source->active_port->name);
}
return pa_strbuf_tostring_free(s);
diff --git a/src/pulsecore/conf-parser.c b/src/pulsecore/conf-parser.c
index a6eb581c..2dc9a223 100644
--- a/src/pulsecore/conf-parser.c
+++ b/src/pulsecore/conf-parser.c
@@ -40,19 +40,35 @@
#define COMMENTS "#;\n"
/* Run the user supplied parser for an assignment */
-static int next_assignment(const char *filename, unsigned line, const char *section, const pa_config_item *t, const char *lvalue, const char *rvalue, void *userdata) {
+static int next_assignment(
+ const char *filename,
+ unsigned line,
+ const char *section,
+ const pa_config_item *t,
+ const char *lvalue,
+ const char *rvalue,
+ void *userdata) {
+
pa_assert(filename);
pa_assert(t);
pa_assert(lvalue);
pa_assert(rvalue);
- for (; t->parse; t++)
- if (!t->lvalue ||
- (pa_streq(lvalue, t->lvalue) &&
- ((!section && !t->section) || pa_streq(section, t->section))))
- return t->parse(filename, line, section, lvalue, rvalue, t->data, userdata);
+ for (; t->parse; t++) {
+
+ if (t->lvalue && !pa_streq(lvalue, t->lvalue))
+ continue;
+
+ if (t->section && !section)
+ continue;
+
+ if (t->section && !pa_streq(section, t->section))
+ continue;
+
+ return t->parse(filename, line, section, lvalue, rvalue, t->data, userdata);
+ }
- pa_log("[%s:%u] Unknown lvalue '%s' in section '%s'.", filename, line, lvalue, pa_strnull(section));
+ pa_log("[%s:%u] Unknown lvalue '%s' in section '%s'.", filename, line, lvalue, pa_strna(section));
return -1;
}
@@ -96,6 +112,25 @@ static int parse_line(const char *filename, unsigned line, char **section, const
if (!*b)
return 0;
+ if (pa_startswith(b, ".include ")) {
+ char *path, *fn;
+ int r;
+
+ fn = strip(b+9);
+ if (!pa_is_path_absolute(fn)) {
+ const char *k;
+ if ((k = strrchr(filename, '/'))) {
+ char *dir = pa_xstrndup(filename, k-filename);
+ fn = path = pa_sprintf_malloc("%s" PA_PATH_SEP "%s", dir, fn);
+ pa_xfree(dir);
+ }
+ }
+
+ r = pa_config_parse(fn, NULL, t, userdata);
+ pa_xfree(path);
+ return r;
+ }
+
if (*b == '[') {
size_t k;
@@ -135,6 +170,7 @@ int pa_config_parse(const char *filename, FILE *f, const pa_config_item *t, void
if (!f && !(f = fopen(filename, "r"))) {
if (errno == ENOENT) {
+ pa_log_debug("Failed to open configuration file '%s': %s", filename, pa_cstrerror(errno));
r = 0;
goto finish;
}
diff --git a/src/pulsecore/core-util.c b/src/pulsecore/core-util.c
index b747cd84..a71ba0b0 100644
--- a/src/pulsecore/core-util.c
+++ b/src/pulsecore/core-util.c
@@ -2732,3 +2732,48 @@ void pa_disable_sigpipe(void) {
}
#endif
}
+
+void pa_xfreev(void**a) {
+ void **p;
+
+ if (!a)
+ return;
+
+ for (p = a; *p; p++)
+ pa_xfree(*p);
+
+ pa_xfree(a);
+}
+
+char **pa_split_spaces_strv(const char *s) {
+ char **t, *e;
+ unsigned i = 0, n = 8;
+ const char *state = NULL;
+
+ t = pa_xnew(char*, n);
+ while ((e = pa_split_spaces(s, &state))) {
+ t[i++] = e;
+
+ if (i >= n) {
+ n *= 2;
+ t = pa_xrenew(char*, t, n);
+ }
+ }
+
+ if (i <= 0) {
+ pa_xfree(t);
+ return NULL;
+ }
+
+ t[i] = NULL;
+ return t;
+}
+
+char* pa_maybe_prefix_path(const char *path, const char *prefix) {
+ pa_assert(path);
+
+ if (pa_is_path_absolute(path))
+ return pa_xstrdup(path);
+
+ return pa_sprintf_malloc("%s" PA_PATH_SEP "%s", prefix, path);
+}
diff --git a/src/pulsecore/core-util.h b/src/pulsecore/core-util.h
index d073b750..b841edbb 100644
--- a/src/pulsecore/core-util.h
+++ b/src/pulsecore/core-util.h
@@ -229,4 +229,14 @@ char *pa_realpath(const char *path);
void pa_disable_sigpipe(void);
+void pa_xfreev(void**a);
+
+static inline void pa_xstrfreev(char **a) {
+ pa_xfreev((void**) a);
+}
+
+char **pa_split_spaces_strv(const char *s);
+
+char* pa_maybe_prefix_path(const char *path, const char *prefix);
+
#endif
diff --git a/src/pulsecore/database-gdbm.c b/src/pulsecore/database-gdbm.c
index aeaac64b..e65125d3 100644
--- a/src/pulsecore/database-gdbm.c
+++ b/src/pulsecore/database-gdbm.c
@@ -71,10 +71,13 @@ pa_database* pa_database_open(const char *fn, pa_bool_t for_write) {
/* We include the host identifier in the file name because gdbm
* files are CPU dependant, and we don't want things to go wrong
* if we are on a multiarch system. */
-
path = pa_sprintf_malloc("%s."CANONICAL_HOST".gdbm", fn);
errno = 0;
- f = gdbm_open((char*) path, 0, GDBM_NOLOCK | (for_write ? GDBM_WRCREAT : GDBM_READER), 0644, NULL);
+
+ /* We need to set the block size explicitly here, since otherwise
+ * gdbm takes the native block size of the underlying file system
+ * which might be incredibly large. */
+ f = gdbm_open((char*) path, 1024, GDBM_NOLOCK | (for_write ? GDBM_WRCREAT : GDBM_READER), 0644, NULL);
if (f)
pa_log_debug("Opened GDBM database '%s'", path);
diff --git a/src/pulsecore/hashmap.c b/src/pulsecore/hashmap.c
index e957c5ba..1fac97eb 100644
--- a/src/pulsecore/hashmap.c
+++ b/src/pulsecore/hashmap.c
@@ -237,6 +237,39 @@ at_end:
return NULL;
}
+void *pa_hashmap_iterate_backwards(pa_hashmap *h, void **state, const void **key) {
+ struct hashmap_entry *e;
+
+ pa_assert(h);
+ pa_assert(state);
+
+ if (*state == (void*) -1)
+ goto at_beginning;
+
+ if (!*state && !h->iterate_list_tail)
+ goto at_beginning;
+
+ e = *state ? *state : h->iterate_list_tail;
+
+ if (e->iterate_previous)
+ *state = e->iterate_previous;
+ else
+ *state = (void*) -1;
+
+ if (key)
+ *key = e->key;
+
+ return e->value;
+
+at_beginning:
+ *state = (void *) -1;
+
+ if (key)
+ *key = NULL;
+
+ return NULL;
+}
+
void* pa_hashmap_first(pa_hashmap *h) {
pa_assert(h);
@@ -246,6 +279,15 @@ void* pa_hashmap_first(pa_hashmap *h) {
return h->iterate_list_head->value;
}
+void* pa_hashmap_last(pa_hashmap *h) {
+ pa_assert(h);
+
+ if (!h->iterate_list_tail)
+ return NULL;
+
+ return h->iterate_list_tail->value;
+}
+
void* pa_hashmap_steal_first(pa_hashmap *h) {
void *data;
diff --git a/src/pulsecore/hashmap.h b/src/pulsecore/hashmap.h
index 828e2448..ac2092a6 100644
--- a/src/pulsecore/hashmap.h
+++ b/src/pulsecore/hashmap.h
@@ -26,7 +26,8 @@
/* Simple Implementation of a hash table. Memory management is the
* user's job. It's a good idea to have the key pointer point to a
- * string in the value data. */
+ * string in the value data. The insertion order is preserved when
+ * iterating. */
typedef struct pa_hashmap pa_hashmap;
@@ -59,14 +60,24 @@ pa_bool_t pa_hashmap_isempty(pa_hashmap *h);
returned. */
void *pa_hashmap_iterate(pa_hashmap *h, void **state, const void**key);
+/* Same as pa_hashmap_iterate() but goes backwards */
+void *pa_hashmap_iterate_backwards(pa_hashmap *h, void **state, const void**key);
+
/* Remove the oldest entry in the hashmap and return it */
void *pa_hashmap_steal_first(pa_hashmap *h);
/* Return the oldest entry in the hashmap */
void* pa_hashmap_first(pa_hashmap *h);
+/* Return the newest entry in the hashmap */
+void* pa_hashmap_last(pa_hashmap *h);
+
/* A macro to ease iteration through all entries */
#define PA_HASHMAP_FOREACH(e, h, state) \
for ((state) = NULL, (e) = pa_hashmap_iterate((h), &(state), NULL); (e); (e) = pa_hashmap_iterate((h), &(state), NULL))
+/* A macro to ease iteration through all entries, backwards */
+#define PA_HASHMAP_FOREACH_BACKWARDS(e, h, state) \
+ for ((state) = NULL, (e) = pa_hashmap_iterate_backwards((h), &(state), NULL); (e); (e) = pa_hashmap_iterate_backwards((h), &(state), NULL))
+
#endif
diff --git a/src/pulsecore/idxset.c b/src/pulsecore/idxset.c
index 352ac977..408011f6 100644
--- a/src/pulsecore/idxset.c
+++ b/src/pulsecore/idxset.c
@@ -453,3 +453,17 @@ pa_bool_t pa_idxset_isempty(pa_idxset *s) {
return s->n_entries == 0;
}
+
+pa_idxset *pa_idxset_copy(pa_idxset *s) {
+ pa_idxset *copy;
+ struct idxset_entry *i;
+
+ pa_assert(s);
+
+ copy = pa_idxset_new(s->hash_func, s->compare_func);
+
+ for (i = s->iterate_list_head; i; i = i->iterate_next)
+ pa_idxset_put(copy, i->data, NULL);
+
+ return copy;
+}
diff --git a/src/pulsecore/idxset.h b/src/pulsecore/idxset.h
index a6179fcf..d1e68c5c 100644
--- a/src/pulsecore/idxset.h
+++ b/src/pulsecore/idxset.h
@@ -103,6 +103,9 @@ unsigned pa_idxset_size(pa_idxset*s);
/* Return TRUE of the idxset is empty */
pa_bool_t pa_idxset_isempty(pa_idxset *s);
+/* Duplicate the idxset. This will not copy the actual indexes */
+pa_idxset *pa_idxset_copy(pa_idxset *s);
+
/* A macro to ease iteration through all entries */
#define PA_IDXSET_FOREACH(e, s, idx) \
for ((e) = pa_idxset_first((s), &(idx)); (e); (e) = pa_idxset_next((s), &(idx)))
diff --git a/src/pulsecore/native-common.h b/src/pulsecore/native-common.h
index d4d7f3ee..f49abb09 100644
--- a/src/pulsecore/native-common.h
+++ b/src/pulsecore/native-common.h
@@ -165,6 +165,10 @@ enum {
PA_COMMAND_PLAYBACK_BUFFER_ATTR_CHANGED,
PA_COMMAND_RECORD_BUFFER_ATTR_CHANGED,
+ /* Supported since protocol v16 (0.9.16) */
+ PA_COMMAND_SET_SINK_PORT,
+ PA_COMMAND_SET_SOURCE_PORT,
+
PA_COMMAND_MAX
};
diff --git a/src/pulsecore/pdispatch.c b/src/pulsecore/pdispatch.c
index d00106b4..4388831a 100644
--- a/src/pulsecore/pdispatch.c
+++ b/src/pulsecore/pdispatch.c
@@ -304,7 +304,7 @@ int pa_pdispatch_run(pa_pdispatch *pd, pa_packet*packet, const pa_creds *creds,
if (command >= PA_COMMAND_MAX || !(p = command_names[command]))
pa_snprintf((char*) (p = t), sizeof(t), "%u", command);
- pa_log("[%p] Recieved opcode <%s>", pd, p);
+ pa_log("[%p] Received opcode <%s>", pd, p);
}
#endif
@@ -325,7 +325,7 @@ int pa_pdispatch_run(pa_pdispatch *pd, pa_packet*packet, const pa_creds *creds,
(*c)(pd, command, tag, ts, userdata);
} else {
- pa_log("Recieved unsupported command %u", command);
+ pa_log("Received unsupported command %u", command);
goto finish;
}
diff --git a/src/pulsecore/protocol-native.c b/src/pulsecore/protocol-native.c
index e9e2d601..48f7b135 100644
--- a/src/pulsecore/protocol-native.c
+++ b/src/pulsecore/protocol-native.c
@@ -284,6 +284,7 @@ static void command_update_proplist(pa_pdispatch *pd, uint32_t command, uint32_t
static void command_remove_proplist(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
static void command_extension(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
static void command_set_card_profile(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
+static void command_set_sink_or_source_port(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata);
static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = {
[PA_COMMAND_ERROR] = NULL,
@@ -380,6 +381,9 @@ static const pa_pdispatch_cb_t command_table[PA_COMMAND_MAX] = {
[PA_COMMAND_SET_CARD_PROFILE] = command_set_card_profile,
+ [PA_COMMAND_SET_SINK_PORT] = command_set_sink_or_source_port,
+ [PA_COMMAND_SET_SOURCE_PORT] = command_set_sink_or_source_port,
+
[PA_COMMAND_EXTENSION] = command_extension
};
@@ -2841,6 +2845,23 @@ static void sink_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_sin
pa_tagstruct_putu32(t, sink->n_volume_steps);
pa_tagstruct_putu32(t, sink->card ? sink->card->index : PA_INVALID_INDEX);
}
+
+ if (c->version >= 16) {
+ pa_tagstruct_putu32(t, sink->ports ? pa_hashmap_size(sink->ports) : 0);
+
+ if (sink->ports) {
+ void *state;
+ pa_device_port *p;
+
+ PA_HASHMAP_FOREACH(p, sink->ports, state) {
+ pa_tagstruct_puts(t, p->name);
+ pa_tagstruct_puts(t, p->description);
+ pa_tagstruct_putu32(t, p->priority);
+ }
+ }
+
+ pa_tagstruct_puts(t, sink->active_port ? sink->active_port->name : NULL);
+ }
}
static void source_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_source *source) {
@@ -2881,6 +2902,24 @@ static void source_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_s
pa_tagstruct_putu32(t, source->n_volume_steps);
pa_tagstruct_putu32(t, source->card ? source->card->index : PA_INVALID_INDEX);
}
+
+ if (c->version >= 16) {
+
+ pa_tagstruct_putu32(t, source->ports ? pa_hashmap_size(source->ports) : 0);
+
+ if (source->ports) {
+ void *state;
+ pa_device_port *p;
+
+ PA_HASHMAP_FOREACH(p, source->ports, state) {
+ pa_tagstruct_puts(t, p->name);
+ pa_tagstruct_puts(t, p->description);
+ pa_tagstruct_putu32(t, p->priority);
+ }
+ }
+
+ pa_tagstruct_puts(t, source->active_port ? source->active_port->name : NULL);
+ }
}
static void client_fill_tagstruct(pa_native_connection *c, pa_tagstruct *t, pa_client *client) {
@@ -3328,9 +3367,9 @@ static void command_set_volume(
CHECK_VALIDITY(c->pstream, si || sink || source, tag, PA_ERR_NOENTITY);
if (sink)
- pa_sink_set_volume(sink, &volume, TRUE, TRUE, TRUE);
+ pa_sink_set_volume(sink, &volume, TRUE, TRUE, TRUE, TRUE);
else if (source)
- pa_source_set_volume(source, &volume);
+ pa_source_set_volume(source, &volume, TRUE);
else if (si)
pa_sink_input_set_volume(si, &volume, TRUE, TRUE);
@@ -3400,9 +3439,9 @@ static void command_set_mute(
CHECK_VALIDITY(c->pstream, si || sink || source, tag, PA_ERR_NOENTITY);
if (sink)
- pa_sink_set_mute(sink, mute);
+ pa_sink_set_mute(sink, mute, TRUE);
else if (source)
- pa_source_set_mute(source, mute);
+ pa_source_set_mute(source, mute, TRUE);
else if (si)
pa_sink_input_set_mute(si, mute, TRUE);
@@ -4195,6 +4234,7 @@ static void command_set_card_profile(pa_pdispatch *pd, uint32_t command, uint32_
uint32_t idx = PA_INVALID_INDEX;
const char *name = NULL, *profile = NULL;
pa_card *card = NULL;
+ int ret;
pa_native_connection_assert_ref(c);
pa_assert(t);
@@ -4220,11 +4260,69 @@ static void command_set_card_profile(pa_pdispatch *pd, uint32_t command, uint32_
CHECK_VALIDITY(c->pstream, card, tag, PA_ERR_NOENTITY);
- if (pa_card_set_profile(card, profile, TRUE) < 0) {
- pa_pstream_send_error(c->pstream, tag, PA_ERR_INVALID);
+ if ((ret = pa_card_set_profile(card, profile, TRUE)) < 0) {
+ pa_pstream_send_error(c->pstream, tag, -ret);
+ return;
+ }
+
+ pa_pstream_send_simple_ack(c->pstream, tag);
+}
+
+static void command_set_sink_or_source_port(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
+ pa_native_connection *c = PA_NATIVE_CONNECTION(userdata);
+ uint32_t idx = PA_INVALID_INDEX;
+ const char *name = NULL, *port = NULL;
+ int ret;
+
+ pa_native_connection_assert_ref(c);
+ pa_assert(t);
+
+ if (pa_tagstruct_getu32(t, &idx) < 0 ||
+ pa_tagstruct_gets(t, &name) < 0 ||
+ pa_tagstruct_gets(t, &port) < 0 ||
+ !pa_tagstruct_eof(t)) {
+ protocol_error(c);
return;
}
+ CHECK_VALIDITY(c->pstream, c->authorized, tag, PA_ERR_ACCESS);
+ CHECK_VALIDITY(c->pstream, !name || pa_namereg_is_valid_name(name), tag, PA_ERR_INVALID);
+ CHECK_VALIDITY(c->pstream, idx != PA_INVALID_INDEX || name, tag, PA_ERR_INVALID);
+ CHECK_VALIDITY(c->pstream, idx == PA_INVALID_INDEX || !name, tag, PA_ERR_INVALID);
+ CHECK_VALIDITY(c->pstream, !name || idx == PA_INVALID_INDEX, tag, PA_ERR_INVALID);
+
+ if (command == PA_COMMAND_SET_SINK_PORT) {
+ pa_sink *sink;
+
+ if (idx != PA_INVALID_INDEX)
+ sink = pa_idxset_get_by_index(c->protocol->core->sinks, idx);
+ else
+ sink = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SINK);
+
+ CHECK_VALIDITY(c->pstream, sink, tag, PA_ERR_NOENTITY);
+
+ if ((ret = pa_sink_set_port(sink, port, TRUE)) < 0) {
+ pa_pstream_send_error(c->pstream, tag, -ret);
+ return;
+ }
+ } else {
+ pa_source *source;
+
+ pa_assert(command = PA_COMMAND_SET_SOURCE_PORT);
+
+ if (idx != PA_INVALID_INDEX)
+ source = pa_idxset_get_by_index(c->protocol->core->sources, idx);
+ else
+ source = pa_namereg_get(c->protocol->core, name, PA_NAMEREG_SOURCE);
+
+ CHECK_VALIDITY(c->pstream, source, tag, PA_ERR_NOENTITY);
+
+ if ((ret = pa_source_set_port(source, port, TRUE)) < 0) {
+ pa_pstream_send_error(c->pstream, tag, -ret);
+ return;
+ }
+ }
+
pa_pstream_send_simple_ack(c->pstream, tag);
}
diff --git a/src/pulsecore/pstream.c b/src/pulsecore/pstream.c
index ef1105ba..1d4ac177 100644
--- a/src/pulsecore/pstream.c
+++ b/src/pulsecore/pstream.c
@@ -684,7 +684,7 @@ static int do_read(pa_pstream *p) {
flags = ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_FLAGS]);
if (!p->use_shm && (flags & PA_FLAG_SHMMASK) != 0) {
- pa_log_warn("Recieved SHM frame on a socket where SHM is disabled.");
+ pa_log_warn("Received SHM frame on a socket where SHM is disabled.");
return -1;
}
@@ -714,7 +714,7 @@ static int do_read(pa_pstream *p) {
length = ntohl(p->read.descriptor[PA_PSTREAM_DESCRIPTOR_LENGTH]);
if (length > FRAME_SIZE_MAX_ALLOW || length <= 0) {
- pa_log_warn("Recieved invalid frame size: %lu", (unsigned long) length);
+ pa_log_warn("Received invalid frame size: %lu", (unsigned long) length);
return -1;
}
@@ -743,7 +743,7 @@ static int do_read(pa_pstream *p) {
if ((flags & PA_FLAG_SHMMASK) == PA_FLAG_SHMDATA) {
if (length != sizeof(p->read.shm_info)) {
- pa_log_warn("Recieved SHM memblock frame with Invalid frame length.");
+ pa_log_warn("Received SHM memblock frame with Invalid frame length.");
return -1;
}
@@ -758,7 +758,7 @@ static int do_read(pa_pstream *p) {
p->read.data = NULL;
} else {
- pa_log_warn("Recieved memblock frame with invalid flags value.");
+ pa_log_warn("Received memblock frame with invalid flags value.");
return -1;
}
}
diff --git a/src/pulsecore/sample-util.c b/src/pulsecore/sample-util.c
index dda38834..5b8ccf59 100644
--- a/src/pulsecore/sample-util.c
+++ b/src/pulsecore/sample-util.c
@@ -1182,7 +1182,7 @@ pa_memchunk* pa_silence_memchunk_get(pa_silence_cache *cache, pa_mempool *pool,
case PA_SAMPLE_S24LE:
case PA_SAMPLE_S24BE:
case PA_SAMPLE_S24_32LE:
- case PA_SAMPLE_S24_32RE:
+ case PA_SAMPLE_S24_32BE:
case PA_SAMPLE_FLOAT32LE:
case PA_SAMPLE_FLOAT32BE:
cache->blocks[PA_SAMPLE_S16LE] = b = silence_memblock_new(pool, 0);
diff --git a/src/pulsecore/sample-util.h b/src/pulsecore/sample-util.h
index 79af9efc..6a306c11 100644
--- a/src/pulsecore/sample-util.h
+++ b/src/pulsecore/sample-util.h
@@ -25,6 +25,7 @@
#include <pulse/sample.h>
#include <pulse/volume.h>
+#include <pulse/channelmap.h>
#include <pulsecore/memblock.h>
#include <pulsecore/memchunk.h>
@@ -85,4 +86,62 @@ void pa_memchunk_dump_to_file(pa_memchunk *c, const char *fn);
void pa_memchunk_sine(pa_memchunk *c, pa_mempool *pool, unsigned rate, unsigned freq);
+#define PA_CHANNEL_POSITION_MASK_LEFT \
+ (PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_FRONT_LEFT) \
+ | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_REAR_LEFT) \
+ | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER) \
+ | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_SIDE_LEFT) \
+ | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_FRONT_LEFT) \
+ | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_REAR_LEFT)) \
+
+#define PA_CHANNEL_POSITION_MASK_RIGHT \
+ (PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_FRONT_RIGHT) \
+ | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_REAR_RIGHT) \
+ | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER) \
+ | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_SIDE_RIGHT) \
+ | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_FRONT_RIGHT) \
+ | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_REAR_RIGHT))
+
+#define PA_CHANNEL_POSITION_MASK_CENTER \
+ (PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_FRONT_CENTER) \
+ | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_REAR_CENTER) \
+ | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_CENTER) \
+ | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_FRONT_CENTER) \
+ | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_REAR_CENTER))
+
+#define PA_CHANNEL_POSITION_MASK_FRONT \
+ (PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_FRONT_LEFT) \
+ | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_FRONT_RIGHT) \
+ | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_FRONT_CENTER) \
+ | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER) \
+ | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER) \
+ | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_FRONT_LEFT) \
+ | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_FRONT_RIGHT) \
+ | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_FRONT_CENTER))
+
+#define PA_CHANNEL_POSITION_MASK_REAR \
+ (PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_REAR_LEFT) \
+ | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_REAR_RIGHT) \
+ | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_REAR_CENTER) \
+ | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_REAR_LEFT) \
+ | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_REAR_RIGHT) \
+ | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_REAR_CENTER))
+
+#define PA_CHANNEL_POSITION_MASK_SIDE_OR_TOP_CENTER \
+ (PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_SIDE_LEFT) \
+ | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_SIDE_RIGHT) \
+ | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_CENTER))
+
+#define PA_CHANNEL_POSITION_MASK_TOP \
+ (PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_CENTER) \
+ | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_FRONT_LEFT) \
+ | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_FRONT_RIGHT) \
+ | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_FRONT_CENTER) \
+ | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_REAR_LEFT) \
+ | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_REAR_RIGHT) \
+ | PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_TOP_REAR_CENTER))
+
+#define PA_CHANNEL_POSITION_MASK_ALL \
+ ((pa_channel_position_mask_t) (PA_CHANNEL_POSITION_MASK(PA_CHANNEL_POSITION_MAX)-1))
+
#endif
diff --git a/src/pulsecore/sink-input.c b/src/pulsecore/sink-input.c
index 0d05b00a..a5f96351 100644
--- a/src/pulsecore/sink-input.c
+++ b/src/pulsecore/sink-input.c
@@ -442,7 +442,7 @@ void pa_sink_input_unlink(pa_sink_input *i) {
if (i->sink->flags & PA_SINK_FLAT_VOLUME) {
pa_cvolume new_volume;
pa_sink_update_flat_volume(i->sink, &new_volume);
- pa_sink_set_volume(i->sink, &new_volume, FALSE, FALSE, FALSE);
+ pa_sink_set_volume(i->sink, &new_volume, FALSE, FALSE, FALSE, FALSE);
}
if (i->sink->asyncmsgq)
@@ -520,7 +520,7 @@ void pa_sink_input_put(pa_sink_input *i) {
if (i->sink->flags & PA_SINK_FLAT_VOLUME) {
pa_cvolume new_volume;
pa_sink_update_flat_volume(i->sink, &new_volume);
- pa_sink_set_volume(i->sink, &new_volume, FALSE, FALSE, FALSE);
+ pa_sink_set_volume(i->sink, &new_volume, FALSE, FALSE, FALSE, FALSE);
} else
pa_sink_input_set_relative_volume(i, &i->virtual_volume);
@@ -900,7 +900,7 @@ void pa_sink_input_set_volume(pa_sink_input *i, const pa_cvolume *volume, pa_boo
* volumes and update the flat volume of the sink */
pa_sink_update_flat_volume(i->sink, &new_volume);
- pa_sink_set_volume(i->sink, &new_volume, FALSE, TRUE, FALSE);
+ pa_sink_set_volume(i->sink, &new_volume, FALSE, TRUE, FALSE, FALSE);
} else {
@@ -1159,7 +1159,7 @@ int pa_sink_input_start_move(pa_sink_input *i) {
/* We might need to update the sink's volume if we are in flat
* volume mode. */
pa_sink_update_flat_volume(i->sink, &new_volume);
- pa_sink_set_volume(i->sink, &new_volume, FALSE, FALSE, FALSE);
+ pa_sink_set_volume(i->sink, &new_volume, FALSE, FALSE, FALSE, FALSE);
}
pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_START_MOVE, i, 0, NULL) == 0);
@@ -1252,7 +1252,7 @@ int pa_sink_input_finish_move(pa_sink_input *i, pa_sink *dest, pa_bool_t save) {
/* We might need to update the sink's volume if we are in flat volume mode. */
pa_sink_update_flat_volume(i->sink, &new_volume);
- pa_sink_set_volume(i->sink, &new_volume, FALSE, FALSE, FALSE);
+ pa_sink_set_volume(i->sink, &new_volume, FALSE, FALSE, FALSE, FALSE);
}
pa_assert_se(pa_asyncmsgq_send(i->sink->asyncmsgq, PA_MSGOBJECT(i->sink), PA_SINK_MESSAGE_FINISH_MOVE, i, 0, NULL) == 0);
diff --git a/src/pulsecore/sink.c b/src/pulsecore/sink.c
index 13f0e11e..47792293 100644
--- a/src/pulsecore/sink.c
+++ b/src/pulsecore/sink.c
@@ -100,11 +100,51 @@ void pa_sink_new_data_set_muted(pa_sink_new_data *data, pa_bool_t mute) {
data->muted = !!mute;
}
+void pa_sink_new_data_set_port(pa_sink_new_data *data, const char *port) {
+ pa_assert(data);
+
+ pa_xfree(data->active_port);
+ data->active_port = pa_xstrdup(port);
+}
+
void pa_sink_new_data_done(pa_sink_new_data *data) {
pa_assert(data);
- pa_xfree(data->name);
pa_proplist_free(data->proplist);
+
+ if (data->ports) {
+ pa_device_port *p;
+
+ while ((p = pa_hashmap_steal_first(data->ports)))
+ pa_device_port_free(p);
+
+ pa_hashmap_free(data->ports, NULL, NULL);
+ }
+
+ pa_xfree(data->name);
+ pa_xfree(data->active_port);
+}
+
+pa_device_port *pa_device_port_new(const char *name, const char *description, size_t extra) {
+ pa_device_port *p;
+
+ pa_assert(name);
+
+ p = pa_xmalloc(PA_ALIGN(sizeof(pa_device_port)) + extra);
+ p->name = pa_xstrdup(name);
+ p->description = pa_xstrdup(description);
+
+ p->priority = 0;
+
+ return p;
+}
+
+void pa_device_port_free(pa_device_port *p) {
+ pa_assert(p);
+
+ pa_xfree(p->name);
+ pa_xfree(p->description);
+ pa_xfree(p);
}
/* Called from main context */
@@ -118,6 +158,7 @@ static void reset_callbacks(pa_sink *s) {
s->set_mute = NULL;
s->request_rewind = NULL;
s->update_requested_latency = NULL;
+ s->set_port = NULL;
}
/* Called from main context */
@@ -152,6 +193,8 @@ pa_sink* pa_sink_new(
return NULL;
}
+ /* FIXME, need to free s here on failure */
+
pa_return_null_if_fail(!data->driver || pa_utf8_valid(data->driver));
pa_return_null_if_fail(data->name && pa_utf8_valid(data->name) && data->name[0]);
@@ -177,6 +220,7 @@ pa_sink* pa_sink_new(
pa_device_init_description(data->proplist);
pa_device_init_icon(data->proplist, TRUE);
+ pa_device_init_intended_roles(data->proplist);
if (pa_hook_fire(&core->hooks[PA_CORE_HOOK_SINK_FIXATE], data) < 0) {
pa_xfree(s);
@@ -218,6 +262,30 @@ pa_sink* pa_sink_new(
s->asyncmsgq = NULL;
s->rtpoll = NULL;
+ /* As a minor optimization we just steal the list instead of
+ * copying it here */
+ s->ports = data->ports;
+ data->ports = NULL;
+
+ s->active_port = NULL;
+ s->save_port = FALSE;
+
+ if (data->active_port && s->ports)
+ if ((s->active_port = pa_hashmap_get(s->ports, data->active_port)))
+ s->save_port = data->save_port;
+
+ if (!s->active_port && s->ports) {
+ void *state;
+ pa_device_port *p;
+
+ PA_HASHMAP_FOREACH(p, s->ports, state)
+ if (!s->active_port || p->priority > s->active_port->priority)
+ s->active_port = p;
+ }
+
+ s->save_volume = data->save_volume;
+ s->save_muted = data->save_muted;
+
pa_silence_memchunk_get(
&core->silence_cache,
core->mempool,
@@ -466,6 +534,15 @@ static void sink_free(pa_object *o) {
if (s->proplist)
pa_proplist_free(s->proplist);
+ if (s->ports) {
+ pa_device_port *p;
+
+ while ((p = pa_hashmap_steal_first(s->ports)))
+ pa_device_port_free(p);
+
+ pa_hashmap_free(s->ports, NULL, NULL);
+ }
+
pa_xfree(s);
}
@@ -484,6 +561,7 @@ void pa_sink_set_rtpoll(pa_sink *s, pa_rtpoll *p) {
pa_sink_assert_ref(s);
s->rtpoll = p;
+
if (s->monitor_source)
pa_source_set_rtpoll(s->monitor_source, p);
}
@@ -525,15 +603,15 @@ int pa_sink_suspend(pa_sink *s, pa_bool_t suspend, pa_suspend_cause_t cause) {
}
/* Called from main context */
-pa_queue *pa_sink_move_all_start(pa_sink *s) {
- pa_queue *q;
+pa_queue *pa_sink_move_all_start(pa_sink *s, pa_queue *q) {
pa_sink_input *i, *n;
uint32_t idx;
pa_sink_assert_ref(s);
pa_assert(PA_SINK_IS_LINKED(s->state));
- q = pa_queue_new();
+ if (!q)
+ q = pa_queue_new();
for (i = PA_SINK_INPUT(pa_idxset_first(s->inputs, &idx)); i; i = n) {
n = PA_SINK_INPUT(pa_idxset_next(s->inputs, &idx));
@@ -1236,7 +1314,7 @@ void pa_sink_propagate_flat_volume(pa_sink *s) {
}
/* Called from main thread */
-void pa_sink_set_volume(pa_sink *s, const pa_cvolume *volume, pa_bool_t propagate, pa_bool_t sendmsg, pa_bool_t become_reference) {
+void pa_sink_set_volume(pa_sink *s, const pa_cvolume *volume, pa_bool_t propagate, pa_bool_t sendmsg, pa_bool_t become_reference, pa_bool_t save) {
pa_bool_t virtual_volume_changed;
pa_sink_assert_ref(s);
@@ -1247,6 +1325,7 @@ void pa_sink_set_volume(pa_sink *s, const pa_cvolume *volume, pa_bool_t propagat
virtual_volume_changed = !pa_cvolume_equal(volume, &s->virtual_volume);
s->virtual_volume = *volume;
+ s->save_volume = (!virtual_volume_changed && s->save_volume) || save;
if (become_reference)
s->reference_volume = s->virtual_volume;
@@ -1317,15 +1396,17 @@ const pa_cvolume *pa_sink_get_volume(pa_sink *s, pa_bool_t force_refresh, pa_boo
}
/* Called from main thread */
-void pa_sink_volume_changed(pa_sink *s, const pa_cvolume *new_volume) {
+void pa_sink_volume_changed(pa_sink *s, const pa_cvolume *new_volume, pa_bool_t save) {
pa_sink_assert_ref(s);
/* The sink implementor may call this if the volume changed to make sure everyone is notified */
-
- if (pa_cvolume_equal(&s->virtual_volume, new_volume))
+ if (pa_cvolume_equal(&s->virtual_volume, new_volume)) {
+ s->save_volume = s->save_volume || save;
return;
+ }
s->reference_volume = s->virtual_volume = *new_volume;
+ s->save_volume = save;
if (s->flags & PA_SINK_FLAT_VOLUME)
pa_sink_propagate_flat_volume(s);
@@ -1334,7 +1415,7 @@ void pa_sink_volume_changed(pa_sink *s, const pa_cvolume *new_volume) {
}
/* Called from main thread */
-void pa_sink_set_mute(pa_sink *s, pa_bool_t mute) {
+void pa_sink_set_mute(pa_sink *s, pa_bool_t mute, pa_bool_t save) {
pa_bool_t old_muted;
pa_sink_assert_ref(s);
@@ -1342,6 +1423,7 @@ void pa_sink_set_mute(pa_sink *s, pa_bool_t mute) {
old_muted = s->muted;
s->muted = mute;
+ s->save_muted = (old_muted == s->muted && s->save_muted) || save;
if (s->set_mute)
s->set_mute(s);
@@ -1377,15 +1459,19 @@ pa_bool_t pa_sink_get_mute(pa_sink *s, pa_bool_t force_refresh) {
}
/* Called from main thread */
-void pa_sink_mute_changed(pa_sink *s, pa_bool_t new_muted) {
+void pa_sink_mute_changed(pa_sink *s, pa_bool_t new_muted, pa_bool_t save) {
pa_sink_assert_ref(s);
/* The sink implementor may call this if the volume changed to make sure everyone is notified */
- if (s->muted == new_muted)
+ if (s->muted == new_muted) {
+ s->save_muted = s->save_muted || save;
return;
+ }
s->muted = new_muted;
+ s->save_muted = save;
+
pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
}
@@ -1483,7 +1569,7 @@ unsigned pa_sink_check_suspend(pa_sink *s) {
ret = 0;
- for (i = PA_SINK_INPUT(pa_idxset_first(s->inputs, &idx)); i; i = PA_SINK_INPUT(pa_idxset_next(s->inputs, &idx))) {
+ PA_IDXSET_FOREACH(i, s->inputs, idx) {
pa_sink_input_state_t st;
st = pa_sink_input_get_state(i);
@@ -2194,6 +2280,41 @@ size_t pa_sink_get_max_request(pa_sink *s) {
}
/* Called from main context */
+int pa_sink_set_port(pa_sink *s, const char *name, pa_bool_t save) {
+ pa_device_port *port;
+
+ pa_assert(s);
+
+ if (!s->set_port) {
+ pa_log_debug("set_port() operation not implemented for sink %u \"%s\"", s->index, s->name);
+ return -PA_ERR_NOTIMPLEMENTED;
+ }
+
+ if (!s->ports)
+ return -PA_ERR_NOENTITY;
+
+ if (!(port = pa_hashmap_get(s->ports, name)))
+ return -PA_ERR_NOENTITY;
+
+ if (s->active_port == port) {
+ s->save_port = s->save_port || save;
+ return 0;
+ }
+
+ if ((s->set_port(s, port)) < 0)
+ return -PA_ERR_NOENTITY;
+
+ pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SINK|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+
+ pa_log_info("Changed port of sink %u \"%s\" to %s", s->index, s->name, port->name);
+
+ s->active_port = port;
+ s->save_port = save;
+
+ return 0;
+}
+
+/* Called from main context */
pa_bool_t pa_device_init_icon(pa_proplist *p, pa_bool_t is_sink) {
const char *ff, *c, *t = NULL, *s = "", *profile, *bus;
@@ -2287,3 +2408,19 @@ pa_bool_t pa_device_init_description(pa_proplist *p) {
return FALSE;
}
+
+pa_bool_t pa_device_init_intended_roles(pa_proplist *p) {
+ const char *s;
+ pa_assert(p);
+
+ if (pa_proplist_contains(p, PA_PROP_DEVICE_INTENDED_ROLES))
+ return TRUE;
+
+ if ((s = pa_proplist_gets(p, PA_PROP_DEVICE_FORM_FACTOR)))
+ if (pa_streq(s, "handset") || pa_streq(s, "hands-free")) {
+ pa_proplist_sets(p, PA_PROP_DEVICE_INTENDED_ROLES, "phone");
+ return TRUE;
+ }
+
+ return FALSE;
+}
diff --git a/src/pulsecore/sink.h b/src/pulsecore/sink.h
index 4dce3f93..d16fcc01 100644
--- a/src/pulsecore/sink.h
+++ b/src/pulsecore/sink.h
@@ -24,6 +24,7 @@
***/
typedef struct pa_sink pa_sink;
+typedef struct pa_device_port pa_device_port;
#include <inttypes.h>
@@ -49,11 +50,23 @@ static inline pa_bool_t PA_SINK_IS_LINKED(pa_sink_state_t x) {
return x == PA_SINK_RUNNING || x == PA_SINK_IDLE || x == PA_SINK_SUSPENDED;
}
+struct pa_device_port {
+ char *name;
+ char *description;
+
+ unsigned priority;
+
+ /* .. followed by some implementation specific data */
+};
+
+#define PA_DEVICE_PORT_DATA(d) ((void*) ((uint8_t*) d + PA_ALIGN(sizeof(pa_device_port))))
+
struct pa_sink {
pa_msgobject parent;
uint32_t index;
pa_core *core;
+
pa_sink_state_t state;
pa_sink_flags_t flags;
pa_suspend_cause_t suspend_cause;
@@ -83,6 +96,9 @@ struct pa_sink {
pa_bool_t refresh_volume:1;
pa_bool_t refresh_muted:1;
+ pa_bool_t save_port:1;
+ pa_bool_t save_volume:1;
+ pa_bool_t save_muted:1;
pa_asyncmsgq *asyncmsgq;
pa_rtpoll *rtpoll;
@@ -91,6 +107,9 @@ struct pa_sink {
pa_usec_t fixed_latency; /* for sinks with PA_SINK_DYNAMIC_LATENCY this is 0 */
+ pa_hashmap *ports;
+ pa_device_port *active_port;
+
/* Called when the main loop requests a state change. Called from
* main loop context. If returns -1 the state change will be
* inhibited */
@@ -126,6 +145,10 @@ struct pa_sink {
* thread context. */
void (*update_requested_latency)(pa_sink *s); /* dito */
+ /* Called whenever the port shall be changed. Called from main
+ * thread. */
+ int (*set_port)(pa_sink *s, pa_device_port *port); /* dito */
+
/* Contains copies of the above data so that the real-time worker
* thread can work without access locking */
struct {
@@ -192,6 +215,9 @@ typedef struct pa_sink_new_data {
pa_module *module;
pa_card *card;
+ pa_hashmap *ports;
+ char *active_port;
+
pa_sample_spec sample_spec;
pa_channel_map channel_map;
pa_cvolume volume;
@@ -203,6 +229,10 @@ typedef struct pa_sink_new_data {
pa_bool_t muted_is_set:1;
pa_bool_t namereg_fail:1;
+
+ pa_bool_t save_port:1;
+ pa_bool_t save_volume:1;
+ pa_bool_t save_muted:1;
} pa_sink_new_data;
pa_sink_new_data* pa_sink_new_data_init(pa_sink_new_data *data);
@@ -211,6 +241,7 @@ void pa_sink_new_data_set_sample_spec(pa_sink_new_data *data, const pa_sample_sp
void pa_sink_new_data_set_channel_map(pa_sink_new_data *data, const pa_channel_map *map);
void pa_sink_new_data_set_volume(pa_sink_new_data *data, const pa_cvolume *volume);
void pa_sink_new_data_set_muted(pa_sink_new_data *data, pa_bool_t mute);
+void pa_sink_new_data_set_port(pa_sink_new_data *data, const char *port);
void pa_sink_new_data_done(pa_sink_new_data *data);
/*** To be called exclusively by the sink driver, from main context */
@@ -236,11 +267,12 @@ void pa_sink_detach(pa_sink *s);
void pa_sink_attach(pa_sink *s);
void pa_sink_set_soft_volume(pa_sink *s, const pa_cvolume *volume);
-void pa_sink_volume_changed(pa_sink *s, const pa_cvolume *new_volume);
-void pa_sink_mute_changed(pa_sink *s, pa_bool_t new_muted);
+void pa_sink_volume_changed(pa_sink *s, const pa_cvolume *new_volume, pa_bool_t save);
+void pa_sink_mute_changed(pa_sink *s, pa_bool_t new_muted, pa_bool_t save);
pa_bool_t pa_device_init_description(pa_proplist *p);
pa_bool_t pa_device_init_icon(pa_proplist *p, pa_bool_t is_sink);
+pa_bool_t pa_device_init_intended_roles(pa_proplist *p);
/**** May be called by everyone, from main context */
@@ -259,21 +291,23 @@ int pa_sink_suspend_all(pa_core *c, pa_bool_t suspend, pa_suspend_cause_t cause)
void pa_sink_update_flat_volume(pa_sink *s, pa_cvolume *new_volume);
void pa_sink_propagate_flat_volume(pa_sink *s);
-void pa_sink_set_volume(pa_sink *sink, const pa_cvolume *volume, pa_bool_t propagate, pa_bool_t sendmsg, pa_bool_t become_reference);
+void pa_sink_set_volume(pa_sink *sink, const pa_cvolume *volume, pa_bool_t propagate, pa_bool_t sendmsg, pa_bool_t become_reference, pa_bool_t save);
const pa_cvolume *pa_sink_get_volume(pa_sink *sink, pa_bool_t force_refresh, pa_bool_t reference);
-void pa_sink_set_mute(pa_sink *sink, pa_bool_t mute);
+void pa_sink_set_mute(pa_sink *sink, pa_bool_t mute, pa_bool_t save);
pa_bool_t pa_sink_get_mute(pa_sink *sink, pa_bool_t force_refresh);
pa_bool_t pa_sink_update_proplist(pa_sink *s, pa_update_mode_t mode, pa_proplist *p);
+int pa_sink_set_port(pa_sink *s, const char *name, pa_bool_t save);
+
unsigned pa_sink_linked_by(pa_sink *s); /* Number of connected streams */
unsigned pa_sink_used_by(pa_sink *s); /* Number of connected streams which are not corked */
unsigned pa_sink_check_suspend(pa_sink *s); /* Returns how many streams are active that don't allow suspensions */
#define pa_sink_get_state(s) ((s)->state)
/* Moves all inputs away, and stores them in pa_queue */
-pa_queue *pa_sink_move_all_start(pa_sink *s);
+pa_queue *pa_sink_move_all_start(pa_sink *s, pa_queue *q);
void pa_sink_move_all_finish(pa_sink *s, pa_queue *q, pa_bool_t save);
void pa_sink_move_all_fail(pa_queue *q);
@@ -306,4 +340,7 @@ void pa_sink_invalidate_requested_latency(pa_sink *s);
pa_usec_t pa_sink_get_latency_within_thread(pa_sink *s);
+pa_device_port *pa_device_port_new(const char *name, const char *description, size_t extra);
+void pa_device_port_free(pa_device_port *p);
+
#endif
diff --git a/src/pulsecore/source.c b/src/pulsecore/source.c
index 53697c57..1e431160 100644
--- a/src/pulsecore/source.c
+++ b/src/pulsecore/source.c
@@ -93,11 +93,29 @@ void pa_source_new_data_set_muted(pa_source_new_data *data, pa_bool_t mute) {
data->muted = !!mute;
}
+void pa_source_new_data_set_port(pa_source_new_data *data, const char *port) {
+ pa_assert(data);
+
+ pa_xfree(data->active_port);
+ data->active_port = pa_xstrdup(port);
+}
+
void pa_source_new_data_done(pa_source_new_data *data) {
pa_assert(data);
- pa_xfree(data->name);
pa_proplist_free(data->proplist);
+
+ if (data->ports) {
+ pa_device_port *p;
+
+ while ((p = pa_hashmap_steal_first(data->ports)))
+ pa_device_port_free(p);
+
+ pa_hashmap_free(data->ports, NULL, NULL);
+ }
+
+ pa_xfree(data->name);
+ pa_xfree(data->active_port);
}
/* Called from main context */
@@ -110,6 +128,7 @@ static void reset_callbacks(pa_source *s) {
s->get_mute = NULL;
s->set_mute = NULL;
s->update_requested_latency = NULL;
+ s->set_port = NULL;
}
/* Called from main context */
@@ -142,6 +161,8 @@ pa_source* pa_source_new(
return NULL;
}
+ /* FIXME, need to free s here on failure */
+
pa_return_null_if_fail(!data->driver || pa_utf8_valid(data->driver));
pa_return_null_if_fail(data->name && pa_utf8_valid(data->name) && data->name[0]);
@@ -167,6 +188,7 @@ pa_source* pa_source_new(
pa_device_init_description(data->proplist);
pa_device_init_icon(data->proplist, FALSE);
+ pa_device_init_intended_roles(data->proplist);
if (pa_hook_fire(&core->hooks[PA_CORE_HOOK_SOURCE_FIXATE], data) < 0) {
pa_xfree(s);
@@ -209,6 +231,30 @@ pa_source* pa_source_new(
s->asyncmsgq = NULL;
s->rtpoll = NULL;
+ /* As a minor optimization we just steal the list instead of
+ * copying it here */
+ s->ports = data->ports;
+ data->ports = NULL;
+
+ s->active_port = NULL;
+ s->save_port = FALSE;
+
+ if (data->active_port && s->ports)
+ if ((s->active_port = pa_hashmap_get(s->ports, data->active_port)))
+ s->save_port = data->save_port;
+
+ if (!s->active_port && s->ports) {
+ void *state;
+ pa_device_port *p;
+
+ PA_HASHMAP_FOREACH(p, s->ports, state)
+ if (!s->active_port || p->priority > s->active_port->priority)
+ s->active_port = p;
+ }
+
+ s->save_volume = data->save_volume;
+ s->save_muted = data->save_muted;
+
pa_silence_memchunk_get(
&core->silence_cache,
core->mempool,
@@ -399,6 +445,15 @@ static void source_free(pa_object *o) {
if (s->proplist)
pa_proplist_free(s->proplist);
+ if (s->ports) {
+ pa_device_port *p;
+
+ while ((p = pa_hashmap_steal_first(s->ports)))
+ pa_device_port_free(p);
+
+ pa_hashmap_free(s->ports, NULL, NULL);
+ }
+
pa_xfree(s);
}
@@ -471,15 +526,15 @@ int pa_source_sync_suspend(pa_source *s) {
}
/* Called from main context */
-pa_queue *pa_source_move_all_start(pa_source *s) {
- pa_queue *q;
+pa_queue *pa_source_move_all_start(pa_source *s, pa_queue *q) {
pa_source_output *o, *n;
uint32_t idx;
pa_source_assert_ref(s);
pa_assert(PA_SOURCE_IS_LINKED(s->state));
- q = pa_queue_new();
+ if (!q)
+ q = pa_queue_new();
for (o = PA_SOURCE_OUTPUT(pa_idxset_first(s->outputs, &idx)); o; o = n) {
n = PA_SOURCE_OUTPUT(pa_idxset_next(s->outputs, &idx));
@@ -666,7 +721,7 @@ pa_usec_t pa_source_get_latency_within_thread(pa_source *s) {
}
/* Called from main thread */
-void pa_source_set_volume(pa_source *s, const pa_cvolume *volume) {
+void pa_source_set_volume(pa_source *s, const pa_cvolume *volume, pa_bool_t save) {
pa_cvolume old_virtual_volume;
pa_bool_t virtual_volume_changed;
@@ -679,6 +734,7 @@ void pa_source_set_volume(pa_source *s, const pa_cvolume *volume) {
old_virtual_volume = s->virtual_volume;
s->virtual_volume = *volume;
virtual_volume_changed = !pa_cvolume_equal(&old_virtual_volume, &s->virtual_volume);
+ s->save_volume = (!virtual_volume_changed && s->save_volume) || save;
if (s->set_volume) {
pa_cvolume_reset(&s->soft_volume, s->sample_spec.channels);
@@ -724,20 +780,24 @@ const pa_cvolume *pa_source_get_volume(pa_source *s, pa_bool_t force_refresh) {
}
/* Called from main thread */
-void pa_source_volume_changed(pa_source *s, const pa_cvolume *new_volume) {
+void pa_source_volume_changed(pa_source *s, const pa_cvolume *new_volume, pa_bool_t save) {
pa_source_assert_ref(s);
/* The source implementor may call this if the volume changed to make sure everyone is notified */
- if (pa_cvolume_equal(&s->virtual_volume, new_volume))
+ if (pa_cvolume_equal(&s->virtual_volume, new_volume)) {
+ s->save_volume = s->save_volume || save;
return;
+ }
s->virtual_volume = *new_volume;
+ s->save_volume = save;
+
pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
}
/* Called from main thread */
-void pa_source_set_mute(pa_source *s, pa_bool_t mute) {
+void pa_source_set_mute(pa_source *s, pa_bool_t mute, pa_bool_t save) {
pa_bool_t old_muted;
pa_source_assert_ref(s);
@@ -745,6 +805,7 @@ void pa_source_set_mute(pa_source *s, pa_bool_t mute) {
old_muted = s->muted;
s->muted = mute;
+ s->save_muted = (old_muted == s->muted && s->save_muted) || save;
if (s->set_mute)
s->set_mute(s);
@@ -780,15 +841,19 @@ pa_bool_t pa_source_get_mute(pa_source *s, pa_bool_t force_refresh) {
}
/* Called from main thread */
-void pa_source_mute_changed(pa_source *s, pa_bool_t new_muted) {
+void pa_source_mute_changed(pa_source *s, pa_bool_t new_muted, pa_bool_t save) {
pa_source_assert_ref(s);
/* The source implementor may call this if the mute state changed to make sure everyone is notified */
- if (s->muted == new_muted)
+ if (s->muted == new_muted) {
+ s->save_muted = s->save_muted || save;
return;
+ }
s->muted = new_muted;
+ s->save_muted = save;
+
pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
}
@@ -865,7 +930,7 @@ unsigned pa_source_check_suspend(pa_source *s) {
ret = 0;
- for (o = PA_SOURCE_OUTPUT(pa_idxset_first(s->outputs, &idx)); o; o = PA_SOURCE_OUTPUT(pa_idxset_next(s->outputs, &idx))) {
+ PA_IDXSET_FOREACH(o, s->outputs, idx) {
pa_source_output_state_t st;
st = pa_source_output_get_state(o);
@@ -1322,3 +1387,38 @@ size_t pa_source_get_max_rewind(pa_source *s) {
return r;
}
+
+/* Called from main context */
+int pa_source_set_port(pa_source *s, const char *name, pa_bool_t save) {
+ pa_device_port *port;
+
+ pa_assert(s);
+
+ if (!s->set_port) {
+ pa_log_debug("set_port() operation not implemented for sink %u \"%s\"", s->index, s->name);
+ return -PA_ERR_NOTIMPLEMENTED;
+ }
+
+ if (!s->ports)
+ return -PA_ERR_NOENTITY;
+
+ if (!(port = pa_hashmap_get(s->ports, name)))
+ return -PA_ERR_NOENTITY;
+
+ if (s->active_port == port) {
+ s->save_port = s->save_port || save;
+ return 0;
+ }
+
+ if ((s->set_port(s, port)) < 0)
+ return -PA_ERR_NOENTITY;
+
+ pa_subscription_post(s->core, PA_SUBSCRIPTION_EVENT_SOURCE|PA_SUBSCRIPTION_EVENT_CHANGE, s->index);
+
+ pa_log_info("Changed port of source %u \"%s\" to %s", s->index, s->name, port->name);
+
+ s->active_port = port;
+ s->save_port = save;
+
+ return 0;
+}
diff --git a/src/pulsecore/source.h b/src/pulsecore/source.h
index 1fbed70e..7e9fd8b7 100644
--- a/src/pulsecore/source.h
+++ b/src/pulsecore/source.h
@@ -56,6 +56,7 @@ struct pa_source {
uint32_t index;
pa_core *core;
+
pa_source_state_t state;
pa_source_flags_t flags;
pa_suspend_cause_t suspend_cause;
@@ -83,6 +84,10 @@ struct pa_source {
pa_bool_t refresh_volume:1;
pa_bool_t refresh_muted:1;
+ pa_bool_t save_port:1;
+ pa_bool_t save_volume:1;
+ pa_bool_t save_muted:1;
+
pa_asyncmsgq *asyncmsgq;
pa_rtpoll *rtpoll;
@@ -90,6 +95,9 @@ struct pa_source {
pa_usec_t fixed_latency; /* for sources with PA_SOURCE_DYNAMIC_LATENCY this is 0 */
+ pa_hashmap *ports;
+ pa_device_port *active_port;
+
/* Called when the main loop requests a state change. Called from
* main loop context. If returns -1 the state change will be
* inhibited */
@@ -121,6 +129,10 @@ struct pa_source {
* thread context. */
void (*update_requested_latency)(pa_source *s); /* dito */
+ /* Called whenever the port shall be changed. Called from main
+ * thread. */
+ int (*set_port)(pa_source *s, pa_device_port *port); /*dito */
+
/* Contains copies of the above data so that the real-time worker
* thread can work without access locking */
struct {
@@ -174,6 +186,9 @@ typedef struct pa_source_new_data {
pa_module *module;
pa_card *card;
+ pa_hashmap *ports;
+ char *active_port;
+
pa_sample_spec sample_spec;
pa_channel_map channel_map;
pa_cvolume volume;
@@ -185,6 +200,10 @@ typedef struct pa_source_new_data {
pa_bool_t channel_map_is_set:1;
pa_bool_t namereg_fail:1;
+
+ pa_bool_t save_port:1;
+ pa_bool_t save_volume:1;
+ pa_bool_t save_muted:1;
} pa_source_new_data;
pa_source_new_data* pa_source_new_data_init(pa_source_new_data *data);
@@ -193,6 +212,7 @@ void pa_source_new_data_set_sample_spec(pa_source_new_data *data, const pa_sampl
void pa_source_new_data_set_channel_map(pa_source_new_data *data, const pa_channel_map *map);
void pa_source_new_data_set_volume(pa_source_new_data *data, const pa_cvolume *volume);
void pa_source_new_data_set_muted(pa_source_new_data *data, pa_bool_t mute);
+void pa_source_new_data_set_port(pa_source_new_data *data, const char *port);
void pa_source_new_data_done(pa_source_new_data *data);
/*** To be called exclusively by the source driver, from main context */
@@ -217,8 +237,8 @@ void pa_source_detach(pa_source *s);
void pa_source_attach(pa_source *s);
void pa_source_set_soft_volume(pa_source *s, const pa_cvolume *volume);
-void pa_source_volume_changed(pa_source *s, const pa_cvolume *new_volume);
-void pa_source_mute_changed(pa_source *s, pa_bool_t new_muted);
+void pa_source_volume_changed(pa_source *s, const pa_cvolume *new_volume, pa_bool_t save);
+void pa_source_mute_changed(pa_source *s, pa_bool_t new_muted, pa_bool_t save);
int pa_source_sync_suspend(pa_source *s);
@@ -235,20 +255,22 @@ int pa_source_update_status(pa_source*s);
int pa_source_suspend(pa_source *s, pa_bool_t suspend, pa_suspend_cause_t cause);
int pa_source_suspend_all(pa_core *c, pa_bool_t suspend, pa_suspend_cause_t cause);
-void pa_source_set_volume(pa_source *source, const pa_cvolume *volume);
+void pa_source_set_volume(pa_source *source, const pa_cvolume *volume, pa_bool_t save);
const pa_cvolume *pa_source_get_volume(pa_source *source, pa_bool_t force_refresh);
-void pa_source_set_mute(pa_source *source, pa_bool_t mute);
+void pa_source_set_mute(pa_source *source, pa_bool_t mute, pa_bool_t save);
pa_bool_t pa_source_get_mute(pa_source *source, pa_bool_t force_refresh);
pa_bool_t pa_source_update_proplist(pa_source *s, pa_update_mode_t mode, pa_proplist *p);
+int pa_source_set_port(pa_source *s, const char *name, pa_bool_t save);
+
unsigned pa_source_linked_by(pa_source *s); /* Number of connected streams */
unsigned pa_source_used_by(pa_source *s); /* Number of connected streams that are not corked */
unsigned pa_source_check_suspend(pa_source *s); /* Returns how many streams are active that don't allow suspensions */
#define pa_source_get_state(s) ((pa_source_state_t) (s)->state)
/* Moves all inputs away, and stores them in pa_queue */
-pa_queue *pa_source_move_all_start(pa_source *s);
+pa_queue *pa_source_move_all_start(pa_source *s, pa_queue *q);
void pa_source_move_all_finish(pa_source *s, pa_queue *q, pa_bool_t save);
void pa_source_move_all_fail(pa_queue *q);