• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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