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