1 /***
2 This file is part of PulseAudio.
3
4 Copyright 2008 Lennart Poettering
5
6 PulseAudio is free software; you can redistribute it and/or modify
7 it under the terms of the GNU Lesser General Public License as published
8 by the Free Software Foundation; either version 2.1 of the License,
9 or (at your option) any later version.
10
11 PulseAudio is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public License
17 along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
18 ***/
19
20 #ifdef HAVE_CONFIG_H
21 #include <config.h>
22 #endif
23
24 #include <pulse/context.h>
25 #include <pulse/fork-detect.h>
26 #include <pulse/operation.h>
27
28 #include <pulsecore/macro.h>
29 #include <pulsecore/pstream-util.h>
30
31 #include "internal.h"
32 #include "ext-stream-restore.h"
33
34 enum {
35 SUBCOMMAND_TEST,
36 SUBCOMMAND_READ,
37 SUBCOMMAND_WRITE,
38 SUBCOMMAND_DELETE,
39 SUBCOMMAND_SUBSCRIBE,
40 SUBCOMMAND_EVENT
41 };
42
ext_stream_restore_test_cb(pa_pdispatch * pd,uint32_t command,uint32_t tag,pa_tagstruct * t,void * userdata)43 static void ext_stream_restore_test_cb(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
44 pa_operation *o = userdata;
45 uint32_t version = PA_INVALID_INDEX;
46
47 pa_assert(pd);
48 pa_assert(o);
49 pa_assert(PA_REFCNT_VALUE(o) >= 1);
50
51 if (!o->context)
52 goto finish;
53
54 if (command != PA_COMMAND_REPLY) {
55 if (pa_context_handle_error(o->context, command, t, false) < 0)
56 goto finish;
57
58 } else if (pa_tagstruct_getu32(t, &version) < 0 ||
59 !pa_tagstruct_eof(t)) {
60
61 pa_context_fail(o->context, PA_ERR_PROTOCOL);
62 goto finish;
63 }
64
65 if (o->callback) {
66 pa_ext_stream_restore_test_cb_t cb = (pa_ext_stream_restore_test_cb_t) o->callback;
67 cb(o->context, version, o->userdata);
68 }
69
70 finish:
71 pa_operation_done(o);
72 pa_operation_unref(o);
73 }
74
pa_ext_stream_restore_test(pa_context * c,pa_ext_stream_restore_test_cb_t cb,void * userdata)75 pa_operation *pa_ext_stream_restore_test(
76 pa_context *c,
77 pa_ext_stream_restore_test_cb_t cb,
78 void *userdata) {
79
80 uint32_t tag;
81 pa_operation *o;
82 pa_tagstruct *t;
83
84 pa_assert(c);
85 pa_assert(PA_REFCNT_VALUE(c) >= 1);
86
87 PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
88 PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
89 PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 14, PA_ERR_NOTSUPPORTED);
90
91 o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
92
93 t = pa_tagstruct_command(c, PA_COMMAND_EXTENSION, &tag);
94 pa_tagstruct_putu32(t, PA_INVALID_INDEX);
95 pa_tagstruct_puts(t, "module-stream-restore");
96 pa_tagstruct_putu32(t, SUBCOMMAND_TEST);
97 pa_pstream_send_tagstruct(c->pstream, t);
98 pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, ext_stream_restore_test_cb, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
99
100 return o;
101 }
102
ext_stream_restore_read_cb(pa_pdispatch * pd,uint32_t command,uint32_t tag,pa_tagstruct * t,void * userdata)103 static void ext_stream_restore_read_cb(pa_pdispatch *pd, uint32_t command, uint32_t tag, pa_tagstruct *t, void *userdata) {
104 pa_operation *o = userdata;
105 int eol = 1;
106
107 pa_assert(pd);
108 pa_assert(o);
109 pa_assert(PA_REFCNT_VALUE(o) >= 1);
110
111 if (!o->context)
112 goto finish;
113
114 if (command != PA_COMMAND_REPLY) {
115 if (pa_context_handle_error(o->context, command, t, false) < 0)
116 goto finish;
117
118 eol = -1;
119 } else {
120
121 while (!pa_tagstruct_eof(t)) {
122 pa_ext_stream_restore_info i;
123 bool mute = false;
124
125 memset(&i, 0, sizeof(i));
126
127 if (pa_tagstruct_gets(t, &i.name) < 0 ||
128 pa_tagstruct_get_channel_map(t, &i.channel_map) < 0 ||
129 pa_tagstruct_get_cvolume(t, &i.volume) < 0 ||
130 pa_tagstruct_gets(t, &i.device) < 0 ||
131 pa_tagstruct_get_boolean(t, &mute) < 0) {
132
133 pa_context_fail(o->context, PA_ERR_PROTOCOL);
134 goto finish;
135 }
136
137 i.mute = (int) mute;
138
139 if (o->callback) {
140 pa_ext_stream_restore_read_cb_t cb = (pa_ext_stream_restore_read_cb_t) o->callback;
141 cb(o->context, &i, 0, o->userdata);
142 }
143 }
144 }
145
146 if (o->callback) {
147 pa_ext_stream_restore_read_cb_t cb = (pa_ext_stream_restore_read_cb_t) o->callback;
148 cb(o->context, NULL, eol, o->userdata);
149 }
150
151 finish:
152 pa_operation_done(o);
153 pa_operation_unref(o);
154 }
155
pa_ext_stream_restore_read(pa_context * c,pa_ext_stream_restore_read_cb_t cb,void * userdata)156 pa_operation *pa_ext_stream_restore_read(
157 pa_context *c,
158 pa_ext_stream_restore_read_cb_t cb,
159 void *userdata) {
160
161 uint32_t tag;
162 pa_operation *o;
163 pa_tagstruct *t;
164
165 pa_assert(c);
166 pa_assert(PA_REFCNT_VALUE(c) >= 1);
167
168 PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
169 PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
170 PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 14, PA_ERR_NOTSUPPORTED);
171
172 o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
173
174 t = pa_tagstruct_command(c, PA_COMMAND_EXTENSION, &tag);
175 pa_tagstruct_putu32(t, PA_INVALID_INDEX);
176 pa_tagstruct_puts(t, "module-stream-restore");
177 pa_tagstruct_putu32(t, SUBCOMMAND_READ);
178 pa_pstream_send_tagstruct(c->pstream, t);
179 pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, ext_stream_restore_read_cb, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
180
181 return o;
182 }
183
pa_ext_stream_restore_write(pa_context * c,pa_update_mode_t mode,const pa_ext_stream_restore_info data[],unsigned n,int apply_immediately,pa_context_success_cb_t cb,void * userdata)184 pa_operation *pa_ext_stream_restore_write(
185 pa_context *c,
186 pa_update_mode_t mode,
187 const pa_ext_stream_restore_info data[],
188 unsigned n,
189 int apply_immediately,
190 pa_context_success_cb_t cb,
191 void *userdata) {
192
193 uint32_t tag;
194 pa_operation *o = NULL;
195 pa_tagstruct *t = NULL;
196
197 pa_assert(c);
198 pa_assert(PA_REFCNT_VALUE(c) >= 1);
199 pa_assert(mode == PA_UPDATE_MERGE || mode == PA_UPDATE_REPLACE || mode == PA_UPDATE_SET);
200 pa_assert(data);
201
202 PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
203 PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
204 PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 14, PA_ERR_NOTSUPPORTED);
205
206 o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
207
208 t = pa_tagstruct_command(c, PA_COMMAND_EXTENSION, &tag);
209 pa_tagstruct_putu32(t, PA_INVALID_INDEX);
210 pa_tagstruct_puts(t, "module-stream-restore");
211 pa_tagstruct_putu32(t, SUBCOMMAND_WRITE);
212
213 pa_tagstruct_putu32(t, mode);
214 pa_tagstruct_put_boolean(t, apply_immediately);
215
216 for (; n > 0; n--, data++) {
217 if (!data->name || !*data->name)
218 goto fail;
219
220 pa_tagstruct_puts(t, data->name);
221
222 if (data->volume.channels > 0 &&
223 !pa_cvolume_compatible_with_channel_map(&data->volume, &data->channel_map))
224 goto fail;
225
226 pa_tagstruct_put_channel_map(t, &data->channel_map);
227 pa_tagstruct_put_cvolume(t, &data->volume);
228 pa_tagstruct_puts(t, data->device);
229 pa_tagstruct_put_boolean(t, data->mute);
230 }
231
232 pa_pstream_send_tagstruct(c->pstream, t);
233 pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
234
235 return o;
236
237 fail:
238 pa_operation_cancel(o);
239 pa_operation_unref(o);
240
241 pa_tagstruct_free(t);
242
243 pa_context_set_error(c, PA_ERR_INVALID);
244 return NULL;
245 }
246
pa_ext_stream_restore_delete(pa_context * c,const char * const s[],pa_context_success_cb_t cb,void * userdata)247 pa_operation *pa_ext_stream_restore_delete(
248 pa_context *c,
249 const char *const s[],
250 pa_context_success_cb_t cb,
251 void *userdata) {
252
253 uint32_t tag;
254 pa_operation *o = NULL;
255 pa_tagstruct *t = NULL;
256 const char *const *k;
257
258 pa_assert(c);
259 pa_assert(PA_REFCNT_VALUE(c) >= 1);
260 pa_assert(s);
261
262 PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
263 PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
264 PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 14, PA_ERR_NOTSUPPORTED);
265
266 o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
267
268 t = pa_tagstruct_command(c, PA_COMMAND_EXTENSION, &tag);
269 pa_tagstruct_putu32(t, PA_INVALID_INDEX);
270 pa_tagstruct_puts(t, "module-stream-restore");
271 pa_tagstruct_putu32(t, SUBCOMMAND_DELETE);
272
273 for (k = s; *k; k++) {
274 if (!*k || !**k)
275 goto fail;
276
277 pa_tagstruct_puts(t, *k);
278 }
279
280 pa_pstream_send_tagstruct(c->pstream, t);
281 pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
282
283 return o;
284
285 fail:
286 pa_operation_cancel(o);
287 pa_operation_unref(o);
288
289 pa_tagstruct_free(t);
290
291 pa_context_set_error(c, PA_ERR_INVALID);
292 return NULL;
293 }
294
pa_ext_stream_restore_subscribe(pa_context * c,int enable,pa_context_success_cb_t cb,void * userdata)295 pa_operation *pa_ext_stream_restore_subscribe(
296 pa_context *c,
297 int enable,
298 pa_context_success_cb_t cb,
299 void *userdata) {
300
301 uint32_t tag;
302 pa_operation *o;
303 pa_tagstruct *t;
304
305 pa_assert(c);
306 pa_assert(PA_REFCNT_VALUE(c) >= 1);
307
308 PA_CHECK_VALIDITY_RETURN_NULL(c, !pa_detect_fork(), PA_ERR_FORKED);
309 PA_CHECK_VALIDITY_RETURN_NULL(c, c->state == PA_CONTEXT_READY, PA_ERR_BADSTATE);
310 PA_CHECK_VALIDITY_RETURN_NULL(c, c->version >= 14, PA_ERR_NOTSUPPORTED);
311
312 o = pa_operation_new(c, NULL, (pa_operation_cb_t) cb, userdata);
313
314 t = pa_tagstruct_command(c, PA_COMMAND_EXTENSION, &tag);
315 pa_tagstruct_putu32(t, PA_INVALID_INDEX);
316 pa_tagstruct_puts(t, "module-stream-restore");
317 pa_tagstruct_putu32(t, SUBCOMMAND_SUBSCRIBE);
318 pa_tagstruct_put_boolean(t, enable);
319 pa_pstream_send_tagstruct(c->pstream, t);
320 pa_pdispatch_register_reply(c->pdispatch, tag, DEFAULT_TIMEOUT, pa_context_simple_ack_callback, pa_operation_ref(o), (pa_free_cb_t) pa_operation_unref);
321
322 return o;
323 }
324
pa_ext_stream_restore_set_subscribe_cb(pa_context * c,pa_ext_stream_restore_subscribe_cb_t cb,void * userdata)325 void pa_ext_stream_restore_set_subscribe_cb(
326 pa_context *c,
327 pa_ext_stream_restore_subscribe_cb_t cb,
328 void *userdata) {
329
330 pa_assert(c);
331 pa_assert(PA_REFCNT_VALUE(c) >= 1);
332
333 if (pa_detect_fork())
334 return;
335
336 c->ext_stream_restore.callback = cb;
337 c->ext_stream_restore.userdata = userdata;
338 }
339
pa_ext_stream_restore_command(pa_context * c,uint32_t tag,pa_tagstruct * t)340 void pa_ext_stream_restore_command(pa_context *c, uint32_t tag, pa_tagstruct *t) {
341 uint32_t subcommand;
342
343 pa_assert(c);
344 pa_assert(PA_REFCNT_VALUE(c) >= 1);
345 pa_assert(t);
346
347 if (pa_tagstruct_getu32(t, &subcommand) < 0 ||
348 !pa_tagstruct_eof(t)) {
349
350 pa_context_fail(c, PA_ERR_PROTOCOL);
351 return;
352 }
353
354 if (subcommand != SUBCOMMAND_EVENT) {
355 pa_context_fail(c, PA_ERR_PROTOCOL);
356 return;
357 }
358
359 if (c->ext_stream_restore.callback)
360 c->ext_stream_restore.callback(c, c->ext_stream_restore.userdata);
361 }
362