diff options
Diffstat (limited to 'homescreen/src/paclient.cpp')
-rw-r--r-- | homescreen/src/paclient.cpp | 240 |
1 files changed, 240 insertions, 0 deletions
diff --git a/homescreen/src/paclient.cpp b/homescreen/src/paclient.cpp new file mode 100644 index 0000000..59a3742 --- /dev/null +++ b/homescreen/src/paclient.cpp @@ -0,0 +1,240 @@ +/* + * Copyright (C) 2016,2017 Konsulko Group + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "paclient.h" + +#include <QtCore/QDebug> + +PaClient::PaClient() + : m_init(false), m_ml(nullptr), m_mlapi(nullptr), m_ctx(nullptr) +{ +} + +PaClient::~PaClient() +{ + if (m_init) + close(); +} + +void PaClient::close() +{ + if (!m_init) return; + pa_threaded_mainloop_stop(m_ml); + pa_threaded_mainloop_free(m_ml); + m_init = false; +} + +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)); +} + +void PaClient::incDecVolume(const int volume_delta) +{ + pa_operation *o; + pa_context *c = context(); + pa_sink_info *i = getDefaultSinkInfo(); + + if (volume_delta > 0) + pa_cvolume_inc_clamp(&i->volume, volume_delta, 65536); + else + pa_cvolume_dec(&i->volume, abs(volume_delta)); + + o = pa_context_set_sink_volume_by_index(c, i->index, &i->volume, set_sink_volume_cb, NULL); + if (!o) { + qWarning() << "PaClient: set sink #" << i->index << + " volume: " << pa_strerror(pa_context_errno(c)); + return; + } + pa_operation_unref(o); +} + +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; + + PaClient *self = reinterpret_cast<PaClient*>(data); + pa_sink_info *si = self->getDefaultSinkInfo(); + if (i->index == si->index) { + self->setDefaultSinkInfo(i); + pa_cvolume *cvolume = &self->getDefaultSinkInfo()->volume; + emit self->volumeExternallyChanged(pa_cvolume_avg(cvolume)); + } +} + +void get_default_sink_info_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; + + PaClient *self = reinterpret_cast<PaClient*>(data); + self->setDefaultSinkInfo(i); +} + +pa_sink_info *PaClient::getDefaultSinkInfo(void) +{ + return &m_default_sink_info; +} + +void PaClient::setDefaultSinkInfo(const pa_sink_info *i) +{ + m_default_sink_info.index = i->index; + m_default_sink_info.channel_map.channels = i->channel_map.channels; + pa_cvolume *cvolume = &m_default_sink_info.volume; + cvolume->channels = i->volume.channels; + for (int chan = 0; chan < i->channel_map.channels; chan++) { + cvolume->values[chan] = i->volume.values[chan]; + } +} + +void get_server_info_cb(pa_context *c, + const pa_server_info *i, + void *data) +{ + pa_operation *o; + o = pa_context_get_sink_info_by_name(c, i->default_sink_name, get_default_sink_info_cb, data); + if (!o) { + qWarning() << "PaClient: get sink info by name: " << + pa_strerror(pa_context_errno(c)); + return; + } +} + +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: + o = pa_context_get_sink_info_by_index(c, index, get_sink_info_change_cb, data); + if (!o) { + qWarning() << "PaClient: get sink 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; + PaClient *self = reinterpret_cast<PaClient*>(data); + + switch (pa_context_get_state(c)) { + case PA_CONTEXT_CONNECTING: + case PA_CONTEXT_AUTHORIZING: + case PA_CONTEXT_SETTING_NAME: + break; + case PA_CONTEXT_READY: + o = pa_context_get_server_info(c, get_server_info_cb, data); + if (!o) { + qWarning() << "PaClient: get server info: " << + pa_strerror(pa_context_errno(c)); + return; + } + pa_operation_unref(o); + pa_context_set_subscribe_callback(c, subscribe_cb, data); + o = pa_context_subscribe(c, (pa_subscription_mask_t)(PA_SUBSCRIPTION_MASK_SINK), NULL, NULL); + if (!o) { + qWarning() << "PaClient: subscribe: " << + pa_strerror(pa_context_errno(c)); + return; + } + break; + case PA_CONTEXT_TERMINATED: + self->close(); + break; + + case PA_CONTEXT_FAILED: + default: + qCritical() << "PaClient: connection failed: " << + pa_strerror(pa_context_errno(c)); + self->close(); + break; + } +} + +void PaClient::init() +{ + m_ml = pa_threaded_mainloop_new(); + if (!m_ml) { + qCritical("PaClient: failed to create mainloop"); + return; + } + + pa_threaded_mainloop_set_name(m_ml, "PaClient mainloop"); + + m_mlapi = pa_threaded_mainloop_get_api(m_ml); + + lock(); + + m_ctx = pa_context_new(m_mlapi, "HomeScreen"); + if (!m_ctx) { + qCritical("PaClient: failed to create context"); + unlock(); + pa_threaded_mainloop_free(m_ml); + return; + } + pa_context_set_state_callback(m_ctx, context_state_cb, this); + + if (pa_context_connect(m_ctx, 0, (pa_context_flags_t)0, 0) < 0) { + qCritical("PaClient: failed to connect"); + pa_context_unref(m_ctx); + unlock(); + pa_threaded_mainloop_free(m_ml); + return; + } + + if (pa_threaded_mainloop_start(m_ml) != 0) { + qCritical("PaClient: failed to start mainloop"); + pa_context_unref(m_ctx); + unlock(); + pa_threaded_mainloop_free(m_ml); + return; + } + + unlock(); + + m_init = true; +} |