/* * Copyright (C) 2016 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 #include #include #include #include #include #include #include #include #include #include "pacontrolmodel.h" #include "pac.h" /* FIXME: move these into a pac context */ static pa_threaded_mainloop* m; TAILQ_HEAD(pac_cstateq, pac_cstate); static struct pac_cstateq cstateq; static void add_one_cstate(int type, int index, const pa_cvolume *cvolume) { struct pac_cstate *cstate; int i; cstate = pa_xnew(struct pac_cstate, 1); cstate->type = type; cstate->index = index; cstate->cvolume.channels = cvolume->channels; for (i = 0; i < cvolume->channels; i++) cstate->cvolume.values[i] = cvolume->values[i]; TAILQ_INSERT_TAIL(&cstateq, cstate, tailq); } static void get_source_list_cb(pa_context *c, const pa_source_info *i, int eol, void *data) { int chan; if (eol < 0) { fprintf(stderr, "get source list: %s\n", pa_strerror(pa_context_errno(c))); pa_threaded_mainloop_stop(m); return; } if (!eol) { assert(i); add_one_cstate(C_SOURCE, i->index, &i->volume); for (chan = 0; chan < i->channel_map.channels; chan++) { add_one_control(data, i->index, i->description, C_SOURCE, chan, channel_position_string[i->channel_map.map[chan]], i->volume.values[chan]); } } } static void get_sink_list_cb(pa_context *c, const pa_sink_info *i, int eol, void *data) { int chan; if(eol < 0) { fprintf(stderr, "get sink list: %s\n", pa_strerror(pa_context_errno(c))); pa_threaded_mainloop_stop(m); return; } if(!eol) { assert(i); add_one_cstate(C_SINK, i->index, &i->volume); for (chan = 0; chan < i->channel_map.channels; chan++) { add_one_control(data, i->index, i->description, C_SINK, chan, channel_position_string[i->channel_map.map[chan]], i->volume.values[chan]); } } } static void context_state_cb(pa_context *c, void *data) { pa_operation *o; assert(c); switch (pa_context_get_state(c)) { case PA_CONTEXT_CONNECTING: case PA_CONTEXT_AUTHORIZING: case PA_CONTEXT_SETTING_NAME: break; case PA_CONTEXT_READY: /* Fetch the controls of interest */ if (!(o = pa_context_get_source_info_list(c, get_source_list_cb, data))) { fprintf(stderr, "get source info list: %s\n", pa_strerror(pa_context_errno(c))); return; } pa_operation_unref(o); if (!(o = pa_context_get_sink_info_list(c, get_sink_list_cb, data))) { fprintf(stderr, "get sink info list: %s\n", pa_strerror(pa_context_errno(c))); return; } break; case PA_CONTEXT_TERMINATED: pa_threaded_mainloop_stop(m); break; case PA_CONTEXT_FAILED: default: fprintf(stderr, "PA connection failed: %s\n", pa_strerror(pa_context_errno(c))); pa_threaded_mainloop_stop(m); } } static void pac_set_source_volume_cb(pa_context *c, int success, void *userdata __attribute__((unused))) { assert(c); if (!success) fprintf(stderr, "Set source volume: %s\n", pa_strerror(pa_context_errno(c))); } static void pac_set_sink_volume_cb(pa_context *c, int success, void *userdata __attribute__((unused))) { assert(c); if (!success) fprintf(stderr, "Set source volume: %s\n", pa_strerror(pa_context_errno(c))); } void pac_set_volume(pa_context *c, uint32_t type, uint32_t idx, uint32_t channel, uint32_t volume) { pa_operation *o; struct pac_cstate *cstate; TAILQ_FOREACH(cstate, &cstateq, tailq) if (cstate->index == idx) break; cstate->cvolume.values[channel] = volume; if (type == C_SOURCE) { if (!(o = pa_context_set_source_volume_by_index(c, idx, &cstate->cvolume, pac_set_source_volume_cb, NULL))) { fprintf(stderr, "set source #%d channel #%d volume: %s\n", idx, channel, pa_strerror(pa_context_errno(c))); return; } pa_operation_unref(o); } else if (type == C_SINK) { if (!(o = pa_context_set_sink_volume_by_index(c, idx, &cstate->cvolume, pac_set_sink_volume_cb, NULL))) { fprintf(stderr, "set sink #%d channel #%d volume: %s\n", idx, channel, pa_strerror(pa_context_errno(c))); return; } pa_operation_unref(o); } } pa_context *pac_init(void *this, const char *name) { pa_context *c; pa_mainloop_api *mapi; char *server = NULL; char *client = pa_xstrdup(name); TAILQ_INIT(&cstateq); if (!(m = pa_threaded_mainloop_new())) { fprintf(stderr, "pa_mainloop_new() failed.\n"); return NULL; } pa_threaded_mainloop_set_name(m, "pa_mainloop"); mapi = pa_threaded_mainloop_get_api(m); if (!(c = pa_context_new(mapi, client))) { fprintf(stderr, "pa_context_new() failed.\n"); goto exit; } pa_context_set_state_callback(c, context_state_cb, this); if (pa_context_connect(c, server, 0, NULL) < 0) { fprintf(stderr, "pa_context_connect(): %s", pa_strerror(pa_context_errno(c))); goto exit; } if (pa_threaded_mainloop_start(m) < 0) { fprintf(stderr, "pa_mainloop_run() failed.\n"); goto exit; } return c; exit: if (c) pa_context_unref(c); if (m) pa_threaded_mainloop_free(m); pa_xfree(client); return NULL; }