summaryrefslogtreecommitdiffstats
path: root/app/paclient.cpp
diff options
context:
space:
mode:
authorMatt Porter <mporter@konsulko.com>2017-04-20 12:49:15 -0400
committerMatt Porter <mporter@konsulko.com>2017-04-21 12:50:02 -0400
commit288256f33f6298204cd0166cea3202d1fde100da (patch)
tree2aa30a1b09c99e63ce3868ce2fa925472b70bf7c /app/paclient.cpp
parent9e23981d8a86a3530c03be2d541585ce88e7b914 (diff)
Add support for handling external sink/source volume change events
Subscribes to PA volume change events, updating the local cached volume levels, and propagating the change to the UI. This allows changes to sink/source volumes levels from the command line (pactl) or a master volume control to be reflected in the mixer UI controls. Change-Id: I1d570dffeab9fcf4b6ba51e4792852b44a6149ca AGL-Bug: SPEC-549 Signed-off-by: Matt Porter <mporter@konsulko.com>
Diffstat (limited to 'app/paclient.cpp')
-rw-r--r--app/paclient.cpp115
1 files changed, 107 insertions, 8 deletions
diff --git a/app/paclient.cpp b/app/paclient.cpp
index 58d8fad..afe0fad 100644
--- a/app/paclient.cpp
+++ b/app/paclient.cpp
@@ -37,15 +37,19 @@ void PaClient::close()
m_init = false;
}
-static void set_sink_volume_cb(pa_context *c, int success, void *data __attribute__((unused)))
+static void set_sink_volume_cb(pa_context *c, int success, void *data)
{
+ Q_UNUSED(data);
+
if (!success)
qWarning() << "PaClient: set sink volume: " <<
pa_strerror(pa_context_errno(c));
}
-static void set_source_volume_cb(pa_context *c, int success, void *data __attribute__((unused)))
+static void set_source_volume_cb(pa_context *c, int success, void *data)
{
+ Q_UNUSED(data);
+
if (!success)
qWarning() << "PaClient: set source volume: " <<
pa_strerror(pa_context_errno(c));
@@ -112,8 +116,8 @@ void get_sink_list_cb(pa_context *c,
int eol,
void *data)
{
- int chan;
PaClient *self = reinterpret_cast<PaClient*>(data);
+ int chan;
if(eol < 0) {
qWarning() << "PaClient: get sink list: " <<
@@ -132,6 +136,87 @@ void get_sink_list_cb(pa_context *c,
}
}
+void get_sink_info_change_cb(pa_context *c,
+ const pa_sink_info *i,
+ int eol,
+ void *data)
+{
+ Q_UNUSED(c);
+ Q_ASSERT(i);
+ Q_ASSERT(data);
+
+ if (eol) return;
+
+ for (int chan = 0; chan < i->channel_map.channels; chan++) {
+ PaClient *self = reinterpret_cast<PaClient*>(data);
+ QHash<int, pa_cvolume *> states = self->sink_states();
+ pa_cvolume *cvolume = states.value(i->index);
+ // Check each channel for volume change
+ if (cvolume->values[chan] != i->volume.values[chan]) {
+ // On change, update cache and signal
+ cvolume->values[chan] = i->volume.values[chan];
+ emit self->volumeExternallyChanged(C_SINK, i->index, chan, i->volume.values[chan]);
+ }
+ }
+}
+
+void get_source_info_change_cb(pa_context *c,
+ const pa_source_info *i,
+ int eol,
+ void *data)
+{
+ Q_UNUSED(c);
+ Q_ASSERT(i);
+ Q_ASSERT(data);
+
+ if (eol) return;
+
+ for (int chan = 0; chan < i->channel_map.channels; chan++) {
+ PaClient *self = reinterpret_cast<PaClient*>(data);
+ QHash<int, pa_cvolume *> states = self->source_states();
+ pa_cvolume *cvolume = states.value(i->index);
+ // Check each channel for volume change
+ if (cvolume->values[chan] != i->volume.values[chan]) {
+ // On change, update cache and signal
+ cvolume->values[chan] = i->volume.values[chan];
+ emit self->volumeExternallyChanged(C_SOURCE, i->index, chan, i->volume.values[chan]);
+ }
+ }
+}
+
+
+void subscribe_cb(pa_context *c,
+ pa_subscription_event_type_t type,
+ uint32_t index,
+ void *data)
+{
+ pa_operation *o;
+
+ if ((type & PA_SUBSCRIPTION_EVENT_TYPE_MASK) != PA_SUBSCRIPTION_EVENT_CHANGE) {
+ qWarning("PaClient: unhandled subscribe event operation");
+ return;
+ }
+
+ switch (type & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) {
+ case PA_SUBSCRIPTION_EVENT_SINK:
+ if (!(o = pa_context_get_sink_info_by_index(c, index, get_sink_info_change_cb, data))) {
+ qWarning() << "PaClient: get sink info by index: " <<
+ pa_strerror(pa_context_errno(c));
+ return;
+ }
+ break;
+ case PA_SUBSCRIPTION_EVENT_SOURCE:
+ if (!(o = pa_context_get_source_info_by_index(c, index, get_source_info_change_cb, data))) {
+ qWarning() << "PaClient: get source info by index: " <<
+ pa_strerror(pa_context_errno(c));
+ return;
+ }
+ break;
+ default:
+ qWarning("PaClient: unhandled subscribe event facility");
+ }
+}
+
void context_state_cb(pa_context *c, void *data)
{
pa_operation *o;
@@ -155,6 +240,13 @@ void context_state_cb(pa_context *c, void *data)
pa_strerror(pa_context_errno(c));
return;
}
+ pa_operation_unref(o);
+ pa_context_set_subscribe_callback(c, subscribe_cb, data);
+ if (!(o = pa_context_subscribe(c, (pa_subscription_mask_t)(PA_SUBSCRIPTION_MASK_SINK | PA_SUBSCRIPTION_MASK_SOURCE), NULL, NULL))) {
+ qWarning() << "PaClient: subscribe: " <<
+ pa_strerror(pa_context_errno(c));
+ return;
+ }
break;
case PA_CONTEXT_TERMINATED:
self->close();
@@ -212,15 +304,22 @@ void PaClient::init()
void PaClient::addOneControlState(int type, int index, const pa_cvolume *cvolume)
{
- int i;
- pa_cvolume *cvolume_new;
-
- cvolume_new = new pa_cvolume;
+ pa_cvolume *cvolume_new = new pa_cvolume;
cvolume_new->channels = cvolume->channels;
- for (i = 0; i < cvolume->channels; i++)
+ for (int i = 0; i < cvolume->channels; i++)
cvolume_new->values[i] = cvolume->values[i];
if (type == C_SINK)
m_sink_states.insert(index, cvolume_new);
else if (type == C_SOURCE)
m_source_states.insert(index, cvolume_new);
}
+
+QHash<int, pa_cvolume *> PaClient::sink_states(void)
+{
+ return m_sink_states;
+}
+
+QHash<int, pa_cvolume *> PaClient::source_states(void)
+{
+ return m_source_states;
+}