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