2 * Copyright (C) 2016 Konsulko Group
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
26 #include <pulse/pulseaudio.h>
27 #include <sys/queue.h>
29 #include "pacontrolmodel.h"
32 /* FIXME: move these into a pac context */
33 static pa_threaded_mainloop* m;
34 TAILQ_HEAD(pac_cstateq, pac_cstate);
35 static struct pac_cstateq cstateq;
37 static void add_one_cstate(int type, int index, const pa_cvolume *cvolume)
39 struct pac_cstate *cstate;
42 cstate = pa_xnew(struct pac_cstate, 1);
44 cstate->index = index;
45 cstate->cvolume.channels = cvolume->channels;
46 for (i = 0; i < cvolume->channels; i++)
47 cstate->cvolume.values[i] = cvolume->values[i];
49 TAILQ_INSERT_TAIL(&cstateq, cstate, tailq);
52 static void get_source_list_cb(pa_context *c,
53 const pa_source_info *i,
60 fprintf(stderr, "get source list: %s\n",
61 pa_strerror(pa_context_errno(c)));
63 pa_threaded_mainloop_stop(m);
69 add_one_cstate(C_SOURCE, i->index, &i->volume);
70 for (chan = 0; chan < i->channel_map.channels; chan++) {
71 add_one_control(data, i->index, i->description,
73 channel_position_string[i->channel_map.map[chan]],
74 i->volume.values[chan]);
79 static void get_sink_list_cb(pa_context *c,
80 const pa_sink_info *i,
87 fprintf(stderr, "get sink list: %s\n",
88 pa_strerror(pa_context_errno(c)));
90 pa_threaded_mainloop_stop(m);
96 add_one_cstate(C_SINK, i->index, &i->volume);
97 for (chan = 0; chan < i->channel_map.channels; chan++) {
98 add_one_control(data, i->index, i->description,
100 channel_position_string[i->channel_map.map[chan]],
101 i->volume.values[chan]);
106 static void context_state_cb(pa_context *c, void *data) {
110 switch (pa_context_get_state(c)) {
111 case PA_CONTEXT_CONNECTING:
112 case PA_CONTEXT_AUTHORIZING:
113 case PA_CONTEXT_SETTING_NAME:
115 case PA_CONTEXT_READY:
116 /* Fetch the controls of interest */
117 if (!(o = pa_context_get_source_info_list(c, get_source_list_cb, data))) {
118 fprintf(stderr, "get source info list: %s\n",
119 pa_strerror(pa_context_errno(c)));
122 pa_operation_unref(o);
123 if (!(o = pa_context_get_sink_info_list(c, get_sink_list_cb, data))) {
124 fprintf(stderr, "get sink info list: %s\n",
125 pa_strerror(pa_context_errno(c)));
129 case PA_CONTEXT_TERMINATED:
130 pa_threaded_mainloop_stop(m);
132 case PA_CONTEXT_FAILED:
134 fprintf(stderr, "PA connection failed: %s\n",
135 pa_strerror(pa_context_errno(c)));
136 pa_threaded_mainloop_stop(m);
140 static void pac_set_source_volume_cb(pa_context *c, int success, void *userdata __attribute__((unused))) {
143 fprintf(stderr, "Set source volume: %s\n",
144 pa_strerror(pa_context_errno(c)));
147 static void pac_set_sink_volume_cb(pa_context *c, int success, void *userdata __attribute__((unused))) {
150 fprintf(stderr, "Set source volume: %s\n",
151 pa_strerror(pa_context_errno(c)));
154 void pac_set_volume(pa_context *c, uint32_t type, uint32_t idx, uint32_t channel, uint32_t volume)
157 struct pac_cstate *cstate;
159 TAILQ_FOREACH(cstate, &cstateq, tailq)
160 if (cstate->index == idx)
162 cstate->cvolume.values[channel] = volume;
164 if (type == C_SOURCE) {
165 if (!(o = pa_context_set_source_volume_by_index(c, idx, &cstate->cvolume, pac_set_source_volume_cb, NULL))) {
166 fprintf(stderr, "set source #%d channel #%d volume: %s\n",
167 idx, channel, pa_strerror(pa_context_errno(c)));
170 pa_operation_unref(o);
171 } else if (type == C_SINK) {
172 if (!(o = pa_context_set_sink_volume_by_index(c, idx, &cstate->cvolume, pac_set_sink_volume_cb, NULL))) {
173 fprintf(stderr, "set sink #%d channel #%d volume: %s\n",
174 idx, channel, pa_strerror(pa_context_errno(c)));
177 pa_operation_unref(o);
181 pa_context *pac_init(void *this, const char *name) {
183 pa_mainloop_api *mapi;
185 char *client = pa_xstrdup(name);
187 TAILQ_INIT(&cstateq);
189 if (!(m = pa_threaded_mainloop_new())) {
190 fprintf(stderr, "pa_mainloop_new() failed.\n");
194 pa_threaded_mainloop_set_name(m, "pa_mainloop");
195 mapi = pa_threaded_mainloop_get_api(m);
197 if (!(c = pa_context_new(mapi, client))) {
198 fprintf(stderr, "pa_context_new() failed.\n");
202 pa_context_set_state_callback(c, context_state_cb, this);
203 if (pa_context_connect(c, server, 0, NULL) < 0) {
204 fprintf(stderr, "pa_context_connect(): %s", pa_strerror(pa_context_errno(c)));
208 if (pa_threaded_mainloop_start(m) < 0) {
209 fprintf(stderr, "pa_mainloop_run() failed.\n");
220 pa_threaded_mainloop_free(m);