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
34enum {
35    SUBCOMMAND_TEST,
36    SUBCOMMAND_READ,
37    SUBCOMMAND_WRITE,
38    SUBCOMMAND_DELETE,
39    SUBCOMMAND_SUBSCRIBE,
40    SUBCOMMAND_EVENT
41};
42
43static 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
70finish:
71    pa_operation_done(o);
72    pa_operation_unref(o);
73}
74
75pa_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
103static 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
151finish:
152    pa_operation_done(o);
153    pa_operation_unref(o);
154}
155
156pa_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
184pa_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
237fail:
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
247pa_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
285fail:
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
295pa_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
325void 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
340void 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