diff options
author | Matt Porter <mporter@konsulko.com> | 2017-04-20 12:49:15 -0400 |
---|---|---|
committer | Matt Porter <mporter@konsulko.com> | 2017-04-21 12:50:02 -0400 |
commit | 288256f33f6298204cd0166cea3202d1fde100da (patch) | |
tree | 2aa30a1b09c99e63ce3868ce2fa925472b70bf7c /app/paclient.cpp | |
parent | 9e23981d8a86a3530c03be2d541585ce88e7b914 (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.cpp | 115 |
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; +} |