]> code.delx.au - pulseaudio/blob - src/modules/module-stream-restore.c
Merge branch 'master-tx'
[pulseaudio] / src / modules / module-stream-restore.c
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, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
19 USA.
20 ***/
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <unistd.h>
27 #include <string.h>
28 #include <errno.h>
29 #include <sys/types.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <ctype.h>
33
34 #include <pulse/xmalloc.h>
35 #include <pulse/volume.h>
36 #include <pulse/timeval.h>
37 #include <pulse/util.h>
38 #include <pulse/rtclock.h>
39
40 #include <pulsecore/core-error.h>
41 #include <pulsecore/module.h>
42 #include <pulsecore/core-util.h>
43 #include <pulsecore/modargs.h>
44 #include <pulsecore/log.h>
45 #include <pulsecore/core-subscribe.h>
46 #include <pulsecore/sink-input.h>
47 #include <pulsecore/source-output.h>
48 #include <pulsecore/namereg.h>
49 #include <pulsecore/protocol-native.h>
50 #include <pulsecore/pstream.h>
51 #include <pulsecore/pstream-util.h>
52 #include <pulsecore/database.h>
53
54 #include "module-stream-restore-symdef.h"
55
56 PA_MODULE_AUTHOR("Lennart Poettering");
57 PA_MODULE_DESCRIPTION("Automatically restore the volume/mute/device state of streams");
58 PA_MODULE_VERSION(PACKAGE_VERSION);
59 PA_MODULE_LOAD_ONCE(TRUE);
60 PA_MODULE_USAGE(
61 "restore_device=<Save/restore sinks/sources?> "
62 "restore_volume=<Save/restore volumes?> "
63 "restore_muted=<Save/restore muted states?> "
64 "on_hotplug=<When new device becomes available, recheck streams?> "
65 "on_rescue=<When device becomes unavailable, recheck streams?>");
66
67 #define SAVE_INTERVAL (10 * PA_USEC_PER_SEC)
68 #define IDENTIFICATION_PROPERTY "module-stream-restore.id"
69
70 static const char* const valid_modargs[] = {
71 "restore_device",
72 "restore_volume",
73 "restore_muted",
74 "on_hotplug",
75 "on_rescue",
76 NULL
77 };
78
79 struct userdata {
80 pa_core *core;
81 pa_module *module;
82 pa_subscription *subscription;
83 pa_hook_slot
84 *sink_input_new_hook_slot,
85 *sink_input_fixate_hook_slot,
86 *source_output_new_hook_slot,
87 *sink_put_hook_slot,
88 *source_put_hook_slot,
89 *sink_unlink_hook_slot,
90 *source_unlink_hook_slot,
91 *connection_unlink_hook_slot;
92 pa_time_event *save_time_event;
93 pa_database* database;
94
95 pa_bool_t restore_device:1;
96 pa_bool_t restore_volume:1;
97 pa_bool_t restore_muted:1;
98 pa_bool_t on_hotplug:1;
99 pa_bool_t on_rescue:1;
100
101 pa_native_protocol *protocol;
102 pa_idxset *subscribed;
103 };
104
105 #define ENTRY_VERSION 2
106
107 struct entry {
108 uint8_t version;
109 pa_bool_t muted_valid:1, volume_valid:1, device_valid:1;
110 pa_bool_t muted:1;
111 pa_channel_map channel_map;
112 pa_cvolume volume;
113 char device[PA_NAME_MAX];
114 } PA_GCC_PACKED;
115
116 enum {
117 SUBCOMMAND_TEST,
118 SUBCOMMAND_READ,
119 SUBCOMMAND_WRITE,
120 SUBCOMMAND_DELETE,
121 SUBCOMMAND_SUBSCRIBE,
122 SUBCOMMAND_EVENT
123 };
124
125 static void save_time_callback(pa_mainloop_api*a, pa_time_event* e, const struct timeval *t, void *userdata) {
126 struct userdata *u = userdata;
127
128 pa_assert(a);
129 pa_assert(e);
130 pa_assert(u);
131
132 pa_assert(e == u->save_time_event);
133 u->core->mainloop->time_free(u->save_time_event);
134 u->save_time_event = NULL;
135
136 pa_database_sync(u->database);
137 pa_log_info("Synced.");
138 }
139
140 static char *get_name(pa_proplist *p, const char *prefix) {
141 const char *r;
142 char *t;
143
144 if (!p)
145 return NULL;
146
147 if ((r = pa_proplist_gets(p, IDENTIFICATION_PROPERTY)))
148 return pa_xstrdup(r);
149
150 if ((r = pa_proplist_gets(p, PA_PROP_MEDIA_ROLE)))
151 t = pa_sprintf_malloc("%s-by-media-role:%s", prefix, r);
152 else if ((r = pa_proplist_gets(p, PA_PROP_APPLICATION_ID)))
153 t = pa_sprintf_malloc("%s-by-application-id:%s", prefix, r);
154 else if ((r = pa_proplist_gets(p, PA_PROP_APPLICATION_NAME)))
155 t = pa_sprintf_malloc("%s-by-application-name:%s", prefix, r);
156 else if ((r = pa_proplist_gets(p, PA_PROP_MEDIA_NAME)))
157 t = pa_sprintf_malloc("%s-by-media-name:%s", prefix, r);
158 else
159 t = pa_sprintf_malloc("%s-fallback:%s", prefix, r);
160
161 pa_proplist_sets(p, IDENTIFICATION_PROPERTY, t);
162 return t;
163 }
164
165 static struct entry* read_entry(struct userdata *u, const char *name) {
166 pa_datum key, data;
167 struct entry *e;
168
169 pa_assert(u);
170 pa_assert(name);
171
172 key.data = (char*) name;
173 key.size = strlen(name);
174
175 pa_zero(data);
176
177 if (!pa_database_get(u->database, &key, &data))
178 goto fail;
179
180 if (data.size != sizeof(struct entry)) {
181 /* This is probably just a database upgrade, hence let's not
182 * consider this more than a debug message */
183 pa_log_debug("Database contains entry for stream %s of wrong size %lu != %lu. Probably due to uprade, ignoring.", name, (unsigned long) data.size, (unsigned long) sizeof(struct entry));
184 goto fail;
185 }
186
187 e = (struct entry*) data.data;
188
189 if (e->version != ENTRY_VERSION) {
190 pa_log_debug("Version of database entry for stream %s doesn't match our version. Probably due to upgrade, ignoring.", name);
191 goto fail;
192 }
193
194 if (!memchr(e->device, 0, sizeof(e->device))) {
195 pa_log_warn("Database contains entry for stream %s with missing NUL byte in device name", name);
196 goto fail;
197 }
198
199 if (e->device_valid && !pa_namereg_is_valid_name(e->device)) {
200 pa_log_warn("Invalid device name stored in database for stream %s", name);
201 goto fail;
202 }
203
204 if (e->volume_valid && !pa_channel_map_valid(&e->channel_map)) {
205 pa_log_warn("Invalid channel map stored in database for stream %s", name);
206 goto fail;
207 }
208
209 if (e->volume_valid && (!pa_cvolume_valid(&e->volume) || !pa_cvolume_compatible_with_channel_map(&e->volume, &e->channel_map))) {
210 pa_log_warn("Invalid volume stored in database for stream %s", name);
211 goto fail;
212 }
213
214 return e;
215
216 fail:
217
218 pa_datum_free(&data);
219 return NULL;
220 }
221
222 static void trigger_save(struct userdata *u) {
223 pa_native_connection *c;
224 uint32_t idx;
225
226 for (c = pa_idxset_first(u->subscribed, &idx); c; c = pa_idxset_next(u->subscribed, &idx)) {
227 pa_tagstruct *t;
228
229 t = pa_tagstruct_new(NULL, 0);
230 pa_tagstruct_putu32(t, PA_COMMAND_EXTENSION);
231 pa_tagstruct_putu32(t, 0);
232 pa_tagstruct_putu32(t, u->module->index);
233 pa_tagstruct_puts(t, u->module->name);
234 pa_tagstruct_putu32(t, SUBCOMMAND_EVENT);
235
236 pa_pstream_send_tagstruct(pa_native_connection_get_pstream(c), t);
237 }
238
239 if (u->save_time_event)
240 return;
241
242 u->save_time_event = pa_core_rttime_new(u->core, pa_rtclock_now() + SAVE_INTERVAL, save_time_callback, u);
243 }
244
245 static pa_bool_t entries_equal(const struct entry *a, const struct entry *b) {
246 pa_cvolume t;
247
248 pa_assert(a);
249 pa_assert(b);
250
251 if (a->device_valid != b->device_valid ||
252 (a->device_valid && strncmp(a->device, b->device, sizeof(a->device))))
253 return FALSE;
254
255 if (a->muted_valid != b->muted_valid ||
256 (a->muted_valid && (a->muted != b->muted)))
257 return FALSE;
258
259 t = b->volume;
260 if (a->volume_valid != b->volume_valid ||
261 (a->volume_valid && !pa_cvolume_equal(pa_cvolume_remap(&t, &b->channel_map, &a->channel_map), &a->volume)))
262 return FALSE;
263
264 return TRUE;
265 }
266
267 static void subscribe_callback(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) {
268 struct userdata *u = userdata;
269 struct entry entry, *old;
270 char *name;
271 pa_datum key, data;
272
273 pa_assert(c);
274 pa_assert(u);
275
276 if (t != (PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_NEW) &&
277 t != (PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_CHANGE) &&
278 t != (PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_NEW) &&
279 t != (PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_CHANGE))
280 return;
281
282 pa_zero(entry);
283 entry.version = ENTRY_VERSION;
284
285 if ((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SINK_INPUT) {
286 pa_sink_input *sink_input;
287
288 if (!(sink_input = pa_idxset_get_by_index(c->sink_inputs, idx)))
289 return;
290
291 if (!(name = get_name(sink_input->proplist, "sink-input")))
292 return;
293
294 if ((old = read_entry(u, name)))
295 entry = *old;
296
297 if (sink_input->save_volume) {
298 entry.channel_map = sink_input->channel_map;
299 pa_sink_input_get_volume(sink_input, &entry.volume, FALSE);
300 entry.volume_valid = TRUE;
301 }
302
303 if (sink_input->save_muted) {
304 entry.muted = pa_sink_input_get_mute(sink_input);
305 entry.muted_valid = TRUE;
306 }
307
308 if (sink_input->save_sink) {
309 pa_strlcpy(entry.device, sink_input->sink->name, sizeof(entry.device));
310 entry.device_valid = TRUE;
311 }
312
313 } else {
314 pa_source_output *source_output;
315
316 pa_assert((t & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) == PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT);
317
318 if (!(source_output = pa_idxset_get_by_index(c->source_outputs, idx)))
319 return;
320
321 if (!(name = get_name(source_output->proplist, "source-output")))
322 return;
323
324 if ((old = read_entry(u, name)))
325 entry = *old;
326
327 if (source_output->save_source) {
328 pa_strlcpy(entry.device, source_output->source->name, sizeof(entry.device));
329 entry.device_valid = source_output->save_source;
330 }
331 }
332
333 if (old) {
334
335 if (entries_equal(old, &entry)) {
336 pa_xfree(old);
337 pa_xfree(name);
338 return;
339 }
340
341 pa_xfree(old);
342 }
343
344 key.data = name;
345 key.size = strlen(name);
346
347 data.data = &entry;
348 data.size = sizeof(entry);
349
350 pa_log_info("Storing volume/mute/device for stream %s.", name);
351
352 pa_database_set(u->database, &key, &data, TRUE);
353
354 pa_xfree(name);
355
356 trigger_save(u);
357 }
358
359 static pa_hook_result_t sink_input_new_hook_callback(pa_core *c, pa_sink_input_new_data *new_data, struct userdata *u) {
360 char *name;
361 struct entry *e;
362
363 pa_assert(c);
364 pa_assert(new_data);
365 pa_assert(u);
366 pa_assert(u->restore_device);
367
368 if (!(name = get_name(new_data->proplist, "sink-input")))
369 return PA_HOOK_OK;
370
371 if ((e = read_entry(u, name))) {
372
373 if (e->device_valid) {
374 pa_sink *s;
375
376 if ((s = pa_namereg_get(c, e->device, PA_NAMEREG_SINK))) {
377 if (!new_data->sink) {
378 pa_log_info("Restoring device for stream %s.", name);
379 new_data->sink = s;
380 new_data->save_sink = TRUE;
381 } else
382 pa_log_debug("Not restoring device for stream %s, because already set.", name);
383 }
384 }
385
386 pa_xfree(e);
387 }
388
389 pa_xfree(name);
390
391 return PA_HOOK_OK;
392 }
393
394 static pa_hook_result_t sink_input_fixate_hook_callback(pa_core *c, pa_sink_input_new_data *new_data, struct userdata *u) {
395 char *name;
396 struct entry *e;
397
398 pa_assert(c);
399 pa_assert(new_data);
400 pa_assert(u);
401 pa_assert(u->restore_volume || u->restore_muted);
402
403 if (!(name = get_name(new_data->proplist, "sink-input")))
404 return PA_HOOK_OK;
405
406 if ((e = read_entry(u, name))) {
407
408 if (u->restore_volume && e->volume_valid) {
409
410 if (!new_data->volume_is_set) {
411 pa_cvolume v;
412
413 pa_log_info("Restoring volume for sink input %s.", name);
414
415 v = e->volume;
416 pa_cvolume_remap(&v, &e->channel_map, &new_data->channel_map);
417 pa_sink_input_new_data_set_volume(new_data, &v);
418
419 new_data->volume_is_absolute = FALSE;
420 new_data->save_volume = TRUE;
421 } else
422 pa_log_debug("Not restoring volume for sink input %s, because already set.", name);
423 }
424
425 if (u->restore_muted && e->muted_valid) {
426
427 if (!new_data->muted_is_set) {
428 pa_log_info("Restoring mute state for sink input %s.", name);
429 pa_sink_input_new_data_set_muted(new_data, e->muted);
430 new_data->save_muted = TRUE;
431 } else
432 pa_log_debug("Not restoring mute state for sink input %s, because already set.", name);
433 }
434
435 pa_xfree(e);
436 }
437
438 pa_xfree(name);
439
440 return PA_HOOK_OK;
441 }
442
443 static pa_hook_result_t source_output_new_hook_callback(pa_core *c, pa_source_output_new_data *new_data, struct userdata *u) {
444 char *name;
445 struct entry *e;
446
447 pa_assert(c);
448 pa_assert(new_data);
449 pa_assert(u);
450 pa_assert(u->restore_device);
451
452 if (new_data->direct_on_input)
453 return PA_HOOK_OK;
454
455 if (!(name = get_name(new_data->proplist, "source-output")))
456 return PA_HOOK_OK;
457
458 if ((e = read_entry(u, name))) {
459 pa_source *s;
460
461 if (e->device_valid) {
462 if ((s = pa_namereg_get(c, e->device, PA_NAMEREG_SOURCE))) {
463 if (!new_data->source) {
464 pa_log_info("Restoring device for stream %s.", name);
465 new_data->source = s;
466 new_data->save_source = TRUE;
467 } else
468 pa_log_debug("Not restoring device for stream %s, because already set", name);
469 }
470 }
471
472 pa_xfree(e);
473 }
474
475 pa_xfree(name);
476
477 return PA_HOOK_OK;
478 }
479
480 static pa_hook_result_t sink_put_hook_callback(pa_core *c, pa_sink *sink, struct userdata *u) {
481 pa_sink_input *si;
482 uint32_t idx;
483
484 pa_assert(c);
485 pa_assert(sink);
486 pa_assert(u);
487 pa_assert(u->on_hotplug && u->restore_device);
488
489 PA_IDXSET_FOREACH(si, c->sink_inputs, idx) {
490 char *name;
491 struct entry *e;
492
493 if (si->sink == sink)
494 continue;
495
496 if (si->save_sink)
497 continue;
498
499 if (!(name = get_name(si->proplist, "sink-input")))
500 continue;
501
502 if ((e = read_entry(u, name))) {
503 if (e->device_valid && pa_streq(e->device, sink->name))
504 pa_sink_input_move_to(si, sink, TRUE);
505
506 pa_xfree(e);
507 }
508
509 pa_xfree(name);
510 }
511
512 return PA_HOOK_OK;
513 }
514
515 static pa_hook_result_t source_put_hook_callback(pa_core *c, pa_source *source, struct userdata *u) {
516 pa_source_output *so;
517 uint32_t idx;
518
519 pa_assert(c);
520 pa_assert(source);
521 pa_assert(u);
522 pa_assert(u->on_hotplug && u->restore_device);
523
524 PA_IDXSET_FOREACH(so, c->source_outputs, idx) {
525 char *name;
526 struct entry *e;
527
528 if (so->source == source)
529 continue;
530
531 if (so->save_source)
532 continue;
533
534 if (so->direct_on_input)
535 continue;
536
537 if (!(name = get_name(so->proplist, "source-input")))
538 continue;
539
540 if ((e = read_entry(u, name))) {
541 if (e->device_valid && pa_streq(e->device, source->name))
542 pa_source_output_move_to(so, source, TRUE);
543
544 pa_xfree(e);
545 }
546
547 pa_xfree(name);
548 }
549
550 return PA_HOOK_OK;
551 }
552
553 static pa_hook_result_t sink_unlink_hook_callback(pa_core *c, pa_sink *sink, struct userdata *u) {
554 pa_sink_input *si;
555 uint32_t idx;
556
557 pa_assert(c);
558 pa_assert(sink);
559 pa_assert(u);
560 pa_assert(u->on_rescue && u->restore_device);
561
562 /* There's no point in doing anything if the core is shut down anyway */
563 if (c->state == PA_CORE_SHUTDOWN)
564 return PA_HOOK_OK;
565
566 PA_IDXSET_FOREACH(si, sink->inputs, idx) {
567 char *name;
568 struct entry *e;
569
570 if (!(name = get_name(si->proplist, "sink-input")))
571 continue;
572
573 if ((e = read_entry(u, name))) {
574
575 if (e->device_valid) {
576 pa_sink *d;
577
578 if ((d = pa_namereg_get(c, e->device, PA_NAMEREG_SINK)) && d != sink)
579 pa_sink_input_move_to(si, d, TRUE);
580 }
581
582 pa_xfree(e);
583 }
584
585 pa_xfree(name);
586 }
587
588 return PA_HOOK_OK;
589 }
590
591 static pa_hook_result_t source_unlink_hook_callback(pa_core *c, pa_source *source, struct userdata *u) {
592 pa_source_output *so;
593 uint32_t idx;
594
595 pa_assert(c);
596 pa_assert(source);
597 pa_assert(u);
598 pa_assert(u->on_rescue && u->restore_device);
599
600 /* There's no point in doing anything if the core is shut down anyway */
601 if (c->state == PA_CORE_SHUTDOWN)
602 return PA_HOOK_OK;
603
604 PA_IDXSET_FOREACH(so, source->outputs, idx) {
605 char *name;
606 struct entry *e;
607
608 if (!(name = get_name(so->proplist, "source-output")))
609 continue;
610
611 if ((e = read_entry(u, name))) {
612
613 if (e->device_valid) {
614 pa_source *d;
615
616 if ((d = pa_namereg_get(c, e->device, PA_NAMEREG_SOURCE)) && d != source)
617 pa_source_output_move_to(so, d, TRUE);
618 }
619
620 pa_xfree(e);
621 }
622
623 pa_xfree(name);
624 }
625
626 return PA_HOOK_OK;
627 }
628
629 #define EXT_VERSION 1
630
631 static void apply_entry(struct userdata *u, const char *name, struct entry *e) {
632 pa_sink_input *si;
633 pa_source_output *so;
634 uint32_t idx;
635
636 pa_assert(u);
637 pa_assert(name);
638 pa_assert(e);
639
640 PA_IDXSET_FOREACH(si, u->core->sink_inputs, idx) {
641 char *n;
642 pa_sink *s;
643
644 if (!(n = get_name(si->proplist, "sink-input")))
645 continue;
646
647 if (!pa_streq(name, n)) {
648 pa_xfree(n);
649 continue;
650 }
651 pa_xfree(n);
652
653 if (u->restore_volume && e->volume_valid) {
654 pa_cvolume v;
655
656 v = e->volume;
657 pa_log_info("Restoring volume for sink input %s.", name);
658 pa_cvolume_remap(&v, &e->channel_map, &si->channel_map);
659 pa_sink_input_set_volume(si, &v, TRUE, FALSE);
660 }
661
662 if (u->restore_muted && e->muted_valid) {
663 pa_log_info("Restoring mute state for sink input %s.", name);
664 pa_sink_input_set_mute(si, e->muted, TRUE);
665 }
666
667 if (u->restore_device &&
668 e->device_valid &&
669 (s = pa_namereg_get(u->core, e->device, PA_NAMEREG_SINK))) {
670
671 pa_log_info("Restoring device for stream %s.", name);
672 pa_sink_input_move_to(si, s, TRUE);
673 }
674 }
675
676 PA_IDXSET_FOREACH(so, u->core->source_outputs, idx) {
677 char *n;
678 pa_source *s;
679
680 if (!(n = get_name(so->proplist, "source-output")))
681 continue;
682
683 if (!pa_streq(name, n)) {
684 pa_xfree(n);
685 continue;
686 }
687 pa_xfree(n);
688
689 if (u->restore_device &&
690 e->device_valid &&
691 (s = pa_namereg_get(u->core, e->device, PA_NAMEREG_SOURCE))) {
692
693 pa_log_info("Restoring device for stream %s.", name);
694 pa_source_output_move_to(so, s, TRUE);
695 }
696 }
697 }
698
699 #if 0
700 static void dump_database(struct userdata *u) {
701 pa_datum key;
702 pa_bool_t done;
703
704 done = !pa_database_first(u->database, &key, NULL);
705
706 while (!done) {
707 pa_datum next_key;
708 struct entry *e;
709 char *name;
710
711 done = !pa_database_next(u->database, &key, &next_key, NULL);
712
713 name = pa_xstrndup(key.data, key.size);
714 pa_datum_free(&key);
715
716 if ((e = read_entry(u, name))) {
717 char t[256];
718 pa_log("name=%s", name);
719 pa_log("device=%s %s", e->device, pa_yes_no(e->device_valid));
720 pa_log("channel_map=%s", pa_channel_map_snprint(t, sizeof(t), &e->channel_map));
721 pa_log("volume=%s %s", pa_cvolume_snprint(t, sizeof(t), &e->volume), pa_yes_no(e->volume_valid));
722 pa_log("mute=%s %s", pa_yes_no(e->muted), pa_yes_no(e->volume_valid));
723 pa_xfree(e);
724 }
725
726 pa_xfree(name);
727
728 key = next_key;
729 }
730 }
731 #endif
732
733 static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connection *c, uint32_t tag, pa_tagstruct *t) {
734 struct userdata *u;
735 uint32_t command;
736 pa_tagstruct *reply = NULL;
737
738 pa_assert(p);
739 pa_assert(m);
740 pa_assert(c);
741 pa_assert(t);
742
743 u = m->userdata;
744
745 if (pa_tagstruct_getu32(t, &command) < 0)
746 goto fail;
747
748 reply = pa_tagstruct_new(NULL, 0);
749 pa_tagstruct_putu32(reply, PA_COMMAND_REPLY);
750 pa_tagstruct_putu32(reply, tag);
751
752 switch (command) {
753 case SUBCOMMAND_TEST: {
754 if (!pa_tagstruct_eof(t))
755 goto fail;
756
757 pa_tagstruct_putu32(reply, EXT_VERSION);
758 break;
759 }
760
761 case SUBCOMMAND_READ: {
762 pa_datum key;
763 pa_bool_t done;
764
765 if (!pa_tagstruct_eof(t))
766 goto fail;
767
768 done = !pa_database_first(u->database, &key, NULL);
769
770 while (!done) {
771 pa_datum next_key;
772 struct entry *e;
773 char *name;
774
775 done = !pa_database_next(u->database, &key, &next_key, NULL);
776
777 name = pa_xstrndup(key.data, key.size);
778 pa_datum_free(&key);
779
780 if ((e = read_entry(u, name))) {
781 pa_cvolume r;
782 pa_channel_map cm;
783
784 pa_tagstruct_puts(reply, name);
785 pa_tagstruct_put_channel_map(reply, e->volume_valid ? &e->channel_map : pa_channel_map_init(&cm));
786 pa_tagstruct_put_cvolume(reply, e->volume_valid ? &e->volume : pa_cvolume_init(&r));
787 pa_tagstruct_puts(reply, e->device_valid ? e->device : NULL);
788 pa_tagstruct_put_boolean(reply, e->muted_valid ? e->muted : FALSE);
789
790 pa_xfree(e);
791 }
792
793 pa_xfree(name);
794
795 key = next_key;
796 }
797
798 break;
799 }
800
801 case SUBCOMMAND_WRITE: {
802 uint32_t mode;
803 pa_bool_t apply_immediately = FALSE;
804
805 if (pa_tagstruct_getu32(t, &mode) < 0 ||
806 pa_tagstruct_get_boolean(t, &apply_immediately) < 0)
807 goto fail;
808
809 if (mode != PA_UPDATE_MERGE &&
810 mode != PA_UPDATE_REPLACE &&
811 mode != PA_UPDATE_SET)
812 goto fail;
813
814 if (mode == PA_UPDATE_SET)
815 pa_database_clear(u->database);
816
817 while (!pa_tagstruct_eof(t)) {
818 const char *name, *device;
819 pa_bool_t muted;
820 struct entry entry;
821 pa_datum key, data;
822
823 pa_zero(entry);
824 entry.version = ENTRY_VERSION;
825
826 if (pa_tagstruct_gets(t, &name) < 0 ||
827 pa_tagstruct_get_channel_map(t, &entry.channel_map) ||
828 pa_tagstruct_get_cvolume(t, &entry.volume) < 0 ||
829 pa_tagstruct_gets(t, &device) < 0 ||
830 pa_tagstruct_get_boolean(t, &muted) < 0)
831 goto fail;
832
833 if (!name || !*name)
834 goto fail;
835
836 entry.volume_valid = entry.volume.channels > 0;
837
838 if (entry.volume_valid)
839 if (!pa_cvolume_compatible_with_channel_map(&entry.volume, &entry.channel_map))
840 goto fail;
841
842 entry.muted = muted;
843 entry.muted_valid = TRUE;
844
845 if (device)
846 pa_strlcpy(entry.device, device, sizeof(entry.device));
847 entry.device_valid = !!entry.device[0];
848
849 if (entry.device_valid &&
850 !pa_namereg_is_valid_name(entry.device))
851 goto fail;
852
853 key.data = (char*) name;
854 key.size = strlen(name);
855
856 data.data = &entry;
857 data.size = sizeof(entry);
858
859 if (pa_database_set(u->database, &key, &data, mode == PA_UPDATE_REPLACE) == 0)
860 if (apply_immediately)
861 apply_entry(u, name, &entry);
862 }
863
864 trigger_save(u);
865
866 break;
867 }
868
869 case SUBCOMMAND_DELETE:
870
871 while (!pa_tagstruct_eof(t)) {
872 const char *name;
873 pa_datum key;
874
875 if (pa_tagstruct_gets(t, &name) < 0)
876 goto fail;
877
878 key.data = (char*) name;
879 key.size = strlen(name);
880
881 pa_database_unset(u->database, &key);
882 }
883
884 trigger_save(u);
885
886 break;
887
888 case SUBCOMMAND_SUBSCRIBE: {
889
890 pa_bool_t enabled;
891
892 if (pa_tagstruct_get_boolean(t, &enabled) < 0 ||
893 !pa_tagstruct_eof(t))
894 goto fail;
895
896 if (enabled)
897 pa_idxset_put(u->subscribed, c, NULL);
898 else
899 pa_idxset_remove_by_data(u->subscribed, c, NULL);
900
901 break;
902 }
903
904 default:
905 goto fail;
906 }
907
908 pa_pstream_send_tagstruct(pa_native_connection_get_pstream(c), reply);
909 return 0;
910
911 fail:
912
913 if (reply)
914 pa_tagstruct_free(reply);
915
916 return -1;
917 }
918
919 static pa_hook_result_t connection_unlink_hook_cb(pa_native_protocol *p, pa_native_connection *c, struct userdata *u) {
920 pa_assert(p);
921 pa_assert(c);
922 pa_assert(u);
923
924 pa_idxset_remove_by_data(u->subscribed, c, NULL);
925 return PA_HOOK_OK;
926 }
927
928 int pa__init(pa_module*m) {
929 pa_modargs *ma = NULL;
930 struct userdata *u;
931 char *fname;
932 pa_sink_input *si;
933 pa_source_output *so;
934 uint32_t idx;
935 pa_bool_t restore_device = TRUE, restore_volume = TRUE, restore_muted = TRUE, on_hotplug = TRUE, on_rescue = TRUE;
936
937 pa_assert(m);
938
939 if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
940 pa_log("Failed to parse module arguments");
941 goto fail;
942 }
943
944 if (pa_modargs_get_value_boolean(ma, "restore_device", &restore_device) < 0 ||
945 pa_modargs_get_value_boolean(ma, "restore_volume", &restore_volume) < 0 ||
946 pa_modargs_get_value_boolean(ma, "restore_muted", &restore_muted) < 0 ||
947 pa_modargs_get_value_boolean(ma, "on_hotplug", &on_hotplug) < 0 ||
948 pa_modargs_get_value_boolean(ma, "on_rescue", &on_rescue) < 0) {
949 pa_log("restore_device=, restore_volume=, restore_muted=, on_hotplug= and on_rescue= expect boolean arguments");
950 goto fail;
951 }
952
953 if (!restore_muted && !restore_volume && !restore_device)
954 pa_log_warn("Neither restoring volume, nor restoring muted, nor restoring device enabled!");
955
956 m->userdata = u = pa_xnew0(struct userdata, 1);
957 u->core = m->core;
958 u->module = m;
959 u->restore_device = restore_device;
960 u->restore_volume = restore_volume;
961 u->restore_muted = restore_muted;
962 u->on_hotplug = on_hotplug;
963 u->on_rescue = on_rescue;
964 u->subscribed = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
965
966 u->protocol = pa_native_protocol_get(m->core);
967 pa_native_protocol_install_ext(u->protocol, m, extension_cb);
968
969 u->connection_unlink_hook_slot = pa_hook_connect(&pa_native_protocol_hooks(u->protocol)[PA_NATIVE_HOOK_CONNECTION_UNLINK], PA_HOOK_NORMAL, (pa_hook_cb_t) connection_unlink_hook_cb, u);
970
971 u->subscription = pa_subscription_new(m->core, PA_SUBSCRIPTION_MASK_SINK_INPUT|PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT, subscribe_callback, u);
972
973 if (restore_device) {
974 /* A little bit earlier than module-intended-roles ... */
975 u->sink_input_new_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_NEW], PA_HOOK_EARLY, (pa_hook_cb_t) sink_input_new_hook_callback, u);
976 u->source_output_new_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_OUTPUT_NEW], PA_HOOK_EARLY, (pa_hook_cb_t) source_output_new_hook_callback, u);
977 }
978
979 if (restore_device && on_hotplug) {
980 /* A little bit earlier than module-intended-roles ... */
981 u->sink_put_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_PUT], PA_HOOK_LATE, (pa_hook_cb_t) sink_put_hook_callback, u);
982 u->source_put_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_PUT], PA_HOOK_LATE, (pa_hook_cb_t) source_put_hook_callback, u);
983 }
984
985 if (restore_device && on_rescue) {
986 /* A little bit earlier than module-intended-roles, module-rescue-streams, ... */
987 u->sink_unlink_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_UNLINK], PA_HOOK_LATE, (pa_hook_cb_t) sink_unlink_hook_callback, u);
988 u->source_unlink_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_UNLINK], PA_HOOK_LATE, (pa_hook_cb_t) source_unlink_hook_callback, u);
989 }
990
991 if (restore_volume || restore_muted)
992 u->sink_input_fixate_hook_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_INPUT_FIXATE], PA_HOOK_EARLY, (pa_hook_cb_t) sink_input_fixate_hook_callback, u);
993
994 if (!(fname = pa_state_path("stream-volumes", TRUE)))
995 goto fail;
996
997 if (!(u->database = pa_database_open(fname, TRUE))) {
998 pa_log("Failed to open volume database '%s': %s", fname, pa_cstrerror(errno));
999 pa_xfree(fname);
1000 goto fail;
1001 }
1002
1003 pa_log_info("Sucessfully opened database file '%s'.", fname);
1004 pa_xfree(fname);
1005
1006 PA_IDXSET_FOREACH(si, m->core->sink_inputs, idx)
1007 subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_NEW, si->index, u);
1008
1009 PA_IDXSET_FOREACH(so, m->core->source_outputs, idx)
1010 subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_NEW, so->index, u);
1011
1012 pa_modargs_free(ma);
1013 return 0;
1014
1015 fail:
1016 pa__done(m);
1017
1018 if (ma)
1019 pa_modargs_free(ma);
1020
1021 return -1;
1022 }
1023
1024 void pa__done(pa_module*m) {
1025 struct userdata* u;
1026
1027 pa_assert(m);
1028
1029 if (!(u = m->userdata))
1030 return;
1031
1032 if (u->subscription)
1033 pa_subscription_free(u->subscription);
1034
1035 if (u->sink_input_new_hook_slot)
1036 pa_hook_slot_free(u->sink_input_new_hook_slot);
1037 if (u->sink_input_fixate_hook_slot)
1038 pa_hook_slot_free(u->sink_input_fixate_hook_slot);
1039 if (u->source_output_new_hook_slot)
1040 pa_hook_slot_free(u->source_output_new_hook_slot);
1041
1042 if (u->sink_put_hook_slot)
1043 pa_hook_slot_free(u->sink_put_hook_slot);
1044 if (u->source_put_hook_slot)
1045 pa_hook_slot_free(u->source_put_hook_slot);
1046
1047 if (u->sink_unlink_hook_slot)
1048 pa_hook_slot_free(u->sink_unlink_hook_slot);
1049 if (u->source_unlink_hook_slot)
1050 pa_hook_slot_free(u->source_unlink_hook_slot);
1051
1052 if (u->connection_unlink_hook_slot)
1053 pa_hook_slot_free(u->connection_unlink_hook_slot);
1054
1055 if (u->save_time_event)
1056 u->core->mainloop->time_free(u->save_time_event);
1057
1058 if (u->database)
1059 pa_database_close(u->database);
1060
1061 if (u->protocol) {
1062 pa_native_protocol_remove_ext(u->protocol, m);
1063 pa_native_protocol_unref(u->protocol);
1064 }
1065
1066 if (u->subscribed)
1067 pa_idxset_free(u->subscribed, NULL, NULL);
1068
1069 pa_xfree(u);
1070 }