]> code.delx.au - pulseaudio/blob - src/modules/module-stream-restore.c
Merge commit 'origin/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 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 /* Skip this if it is already in the process of being moved
544 * anyway */
545 if (!si->sink)
546 continue;
547
548 /* It might happen that a stream and a sink are set up at the
549 same time, in which case we want to make sure we don't
550 interfere with that */
551 if (!PA_SINK_INPUT_IS_LINKED(pa_sink_input_get_state(si)))
552 continue;
553
554 if (!(name = get_name(si->proplist, "sink-input")))
555 continue;
556
557 if ((e = read_entry(u, name))) {
558 if (e->device_valid && pa_streq(e->device, sink->name))
559 pa_sink_input_move_to(si, sink, TRUE);
560
561 pa_xfree(e);
562 }
563
564 pa_xfree(name);
565 }
566
567 return PA_HOOK_OK;
568 }
569
570 static pa_hook_result_t source_put_hook_callback(pa_core *c, pa_source *source, struct userdata *u) {
571 pa_source_output *so;
572 uint32_t idx;
573
574 pa_assert(c);
575 pa_assert(source);
576 pa_assert(u);
577 pa_assert(u->on_hotplug && u->restore_device);
578
579 PA_IDXSET_FOREACH(so, c->source_outputs, idx) {
580 char *name;
581 struct entry *e;
582
583 if (so->source == source)
584 continue;
585
586 if (so->save_source)
587 continue;
588
589 if (so->direct_on_input)
590 continue;
591
592 /* Skip this if it is already in the process of being moved anyway */
593 if (!so->source)
594 continue;
595
596 /* It might happen that a stream and a sink are set up at the
597 same time, in which case we want to make sure we don't
598 interfere with that */
599 if (!PA_SOURCE_OUTPUT_IS_LINKED(pa_source_output_get_state(so)))
600 continue;
601
602 if (!(name = get_name(so->proplist, "source-input")))
603 continue;
604
605 if ((e = read_entry(u, name))) {
606 if (e->device_valid && pa_streq(e->device, source->name))
607 pa_source_output_move_to(so, source, TRUE);
608
609 pa_xfree(e);
610 }
611
612 pa_xfree(name);
613 }
614
615 return PA_HOOK_OK;
616 }
617
618 static pa_hook_result_t sink_unlink_hook_callback(pa_core *c, pa_sink *sink, struct userdata *u) {
619 pa_sink_input *si;
620 uint32_t idx;
621
622 pa_assert(c);
623 pa_assert(sink);
624 pa_assert(u);
625 pa_assert(u->on_rescue && u->restore_device);
626
627 /* There's no point in doing anything if the core is shut down anyway */
628 if (c->state == PA_CORE_SHUTDOWN)
629 return PA_HOOK_OK;
630
631 PA_IDXSET_FOREACH(si, sink->inputs, idx) {
632 char *name;
633 struct entry *e;
634
635 if (!si->sink)
636 continue;
637
638 if (!(name = get_name(si->proplist, "sink-input")))
639 continue;
640
641 if ((e = read_entry(u, name))) {
642
643 if (e->device_valid) {
644 pa_sink *d;
645
646 if ((d = pa_namereg_get(c, e->device, PA_NAMEREG_SINK)) &&
647 d != sink &&
648 PA_SINK_IS_LINKED(pa_sink_get_state(d)))
649 pa_sink_input_move_to(si, d, TRUE);
650 }
651
652 pa_xfree(e);
653 }
654
655 pa_xfree(name);
656 }
657
658 return PA_HOOK_OK;
659 }
660
661 static pa_hook_result_t source_unlink_hook_callback(pa_core *c, pa_source *source, struct userdata *u) {
662 pa_source_output *so;
663 uint32_t idx;
664
665 pa_assert(c);
666 pa_assert(source);
667 pa_assert(u);
668 pa_assert(u->on_rescue && u->restore_device);
669
670 /* There's no point in doing anything if the core is shut down anyway */
671 if (c->state == PA_CORE_SHUTDOWN)
672 return PA_HOOK_OK;
673
674 PA_IDXSET_FOREACH(so, source->outputs, idx) {
675 char *name;
676 struct entry *e;
677
678 if (so->direct_on_input)
679 continue;
680
681 if (!so->source)
682 continue;
683
684 if (!(name = get_name(so->proplist, "source-output")))
685 continue;
686
687 if ((e = read_entry(u, name))) {
688
689 if (e->device_valid) {
690 pa_source *d;
691
692 if ((d = pa_namereg_get(c, e->device, PA_NAMEREG_SOURCE)) &&
693 d != source &&
694 PA_SOURCE_IS_LINKED(pa_source_get_state(d)))
695 pa_source_output_move_to(so, d, TRUE);
696 }
697
698 pa_xfree(e);
699 }
700
701 pa_xfree(name);
702 }
703
704 return PA_HOOK_OK;
705 }
706
707 #define EXT_VERSION 1
708
709 static void apply_entry(struct userdata *u, const char *name, struct entry *e) {
710 pa_sink_input *si;
711 pa_source_output *so;
712 uint32_t idx;
713
714 pa_assert(u);
715 pa_assert(name);
716 pa_assert(e);
717
718 PA_IDXSET_FOREACH(si, u->core->sink_inputs, idx) {
719 char *n;
720 pa_sink *s;
721
722 if (!(n = get_name(si->proplist, "sink-input")))
723 continue;
724
725 if (!pa_streq(name, n)) {
726 pa_xfree(n);
727 continue;
728 }
729 pa_xfree(n);
730
731 if (u->restore_volume && e->volume_valid) {
732 pa_cvolume v;
733
734 v = e->volume;
735 pa_log_info("Restoring volume for sink input %s.", name);
736 pa_cvolume_remap(&v, &e->channel_map, &si->channel_map);
737 pa_sink_input_set_volume(si, &v, TRUE, FALSE);
738 }
739
740 if (u->restore_muted && e->muted_valid) {
741 pa_log_info("Restoring mute state for sink input %s.", name);
742 pa_sink_input_set_mute(si, e->muted, TRUE);
743 }
744
745 if (u->restore_device &&
746 e->device_valid &&
747 (s = pa_namereg_get(u->core, e->device, PA_NAMEREG_SINK))) {
748
749 pa_log_info("Restoring device for stream %s.", name);
750 pa_sink_input_move_to(si, s, TRUE);
751 }
752 }
753
754 PA_IDXSET_FOREACH(so, u->core->source_outputs, idx) {
755 char *n;
756 pa_source *s;
757
758 if (!(n = get_name(so->proplist, "source-output")))
759 continue;
760
761 if (!pa_streq(name, n)) {
762 pa_xfree(n);
763 continue;
764 }
765 pa_xfree(n);
766
767 if (u->restore_device &&
768 e->device_valid &&
769 (s = pa_namereg_get(u->core, e->device, PA_NAMEREG_SOURCE))) {
770
771 pa_log_info("Restoring device for stream %s.", name);
772 pa_source_output_move_to(so, s, TRUE);
773 }
774 }
775 }
776
777 #if 0
778 static void dump_database(struct userdata *u) {
779 pa_datum key;
780 pa_bool_t done;
781
782 done = !pa_database_first(u->database, &key, NULL);
783
784 while (!done) {
785 pa_datum next_key;
786 struct entry *e;
787 char *name;
788
789 done = !pa_database_next(u->database, &key, &next_key, NULL);
790
791 name = pa_xstrndup(key.data, key.size);
792 pa_datum_free(&key);
793
794 if ((e = read_entry(u, name))) {
795 char t[256];
796 pa_log("name=%s", name);
797 pa_log("device=%s %s", e->device, pa_yes_no(e->device_valid));
798 pa_log("channel_map=%s", pa_channel_map_snprint(t, sizeof(t), &e->channel_map));
799 pa_log("volume=%s %s", pa_cvolume_snprint(t, sizeof(t), &e->volume), pa_yes_no(e->volume_valid));
800 pa_log("mute=%s %s", pa_yes_no(e->muted), pa_yes_no(e->volume_valid));
801 pa_xfree(e);
802 }
803
804 pa_xfree(name);
805
806 key = next_key;
807 }
808 }
809 #endif
810
811 static int extension_cb(pa_native_protocol *p, pa_module *m, pa_native_connection *c, uint32_t tag, pa_tagstruct *t) {
812 struct userdata *u;
813 uint32_t command;
814 pa_tagstruct *reply = NULL;
815
816 pa_assert(p);
817 pa_assert(m);
818 pa_assert(c);
819 pa_assert(t);
820
821 u = m->userdata;
822
823 if (pa_tagstruct_getu32(t, &command) < 0)
824 goto fail;
825
826 reply = pa_tagstruct_new(NULL, 0);
827 pa_tagstruct_putu32(reply, PA_COMMAND_REPLY);
828 pa_tagstruct_putu32(reply, tag);
829
830 switch (command) {
831 case SUBCOMMAND_TEST: {
832 if (!pa_tagstruct_eof(t))
833 goto fail;
834
835 pa_tagstruct_putu32(reply, EXT_VERSION);
836 break;
837 }
838
839 case SUBCOMMAND_READ: {
840 pa_datum key;
841 pa_bool_t done;
842
843 if (!pa_tagstruct_eof(t))
844 goto fail;
845
846 done = !pa_database_first(u->database, &key, NULL);
847
848 while (!done) {
849 pa_datum next_key;
850 struct entry *e;
851 char *name;
852
853 done = !pa_database_next(u->database, &key, &next_key, NULL);
854
855 name = pa_xstrndup(key.data, key.size);
856 pa_datum_free(&key);
857
858 if ((e = read_entry(u, name))) {
859 pa_cvolume r;
860 pa_channel_map cm;
861
862 pa_tagstruct_puts(reply, name);
863 pa_tagstruct_put_channel_map(reply, e->volume_valid ? &e->channel_map : pa_channel_map_init(&cm));
864 pa_tagstruct_put_cvolume(reply, e->volume_valid ? &e->volume : pa_cvolume_init(&r));
865 pa_tagstruct_puts(reply, e->device_valid ? e->device : NULL);
866 pa_tagstruct_put_boolean(reply, e->muted_valid ? e->muted : FALSE);
867
868 pa_xfree(e);
869 }
870
871 pa_xfree(name);
872
873 key = next_key;
874 }
875
876 break;
877 }
878
879 case SUBCOMMAND_WRITE: {
880 uint32_t mode;
881 pa_bool_t apply_immediately = FALSE;
882
883 if (pa_tagstruct_getu32(t, &mode) < 0 ||
884 pa_tagstruct_get_boolean(t, &apply_immediately) < 0)
885 goto fail;
886
887 if (mode != PA_UPDATE_MERGE &&
888 mode != PA_UPDATE_REPLACE &&
889 mode != PA_UPDATE_SET)
890 goto fail;
891
892 if (mode == PA_UPDATE_SET)
893 pa_database_clear(u->database);
894
895 while (!pa_tagstruct_eof(t)) {
896 const char *name, *device;
897 pa_bool_t muted;
898 struct entry entry;
899 pa_datum key, data;
900
901 pa_zero(entry);
902 entry.version = ENTRY_VERSION;
903
904 if (pa_tagstruct_gets(t, &name) < 0 ||
905 pa_tagstruct_get_channel_map(t, &entry.channel_map) ||
906 pa_tagstruct_get_cvolume(t, &entry.volume) < 0 ||
907 pa_tagstruct_gets(t, &device) < 0 ||
908 pa_tagstruct_get_boolean(t, &muted) < 0)
909 goto fail;
910
911 if (!name || !*name)
912 goto fail;
913
914 entry.volume_valid = entry.volume.channels > 0;
915
916 if (entry.volume_valid)
917 if (!pa_cvolume_compatible_with_channel_map(&entry.volume, &entry.channel_map))
918 goto fail;
919
920 entry.muted = muted;
921 entry.muted_valid = TRUE;
922
923 if (device)
924 pa_strlcpy(entry.device, device, sizeof(entry.device));
925 entry.device_valid = !!entry.device[0];
926
927 if (entry.device_valid &&
928 !pa_namereg_is_valid_name(entry.device))
929 goto fail;
930
931 key.data = (char*) name;
932 key.size = strlen(name);
933
934 data.data = &entry;
935 data.size = sizeof(entry);
936
937 if (pa_database_set(u->database, &key, &data, mode == PA_UPDATE_REPLACE) == 0)
938 if (apply_immediately)
939 apply_entry(u, name, &entry);
940 }
941
942 trigger_save(u);
943
944 break;
945 }
946
947 case SUBCOMMAND_DELETE:
948
949 while (!pa_tagstruct_eof(t)) {
950 const char *name;
951 pa_datum key;
952
953 if (pa_tagstruct_gets(t, &name) < 0)
954 goto fail;
955
956 key.data = (char*) name;
957 key.size = strlen(name);
958
959 pa_database_unset(u->database, &key);
960 }
961
962 trigger_save(u);
963
964 break;
965
966 case SUBCOMMAND_SUBSCRIBE: {
967
968 pa_bool_t enabled;
969
970 if (pa_tagstruct_get_boolean(t, &enabled) < 0 ||
971 !pa_tagstruct_eof(t))
972 goto fail;
973
974 if (enabled)
975 pa_idxset_put(u->subscribed, c, NULL);
976 else
977 pa_idxset_remove_by_data(u->subscribed, c, NULL);
978
979 break;
980 }
981
982 default:
983 goto fail;
984 }
985
986 pa_pstream_send_tagstruct(pa_native_connection_get_pstream(c), reply);
987 return 0;
988
989 fail:
990
991 if (reply)
992 pa_tagstruct_free(reply);
993
994 return -1;
995 }
996
997 static pa_hook_result_t connection_unlink_hook_cb(pa_native_protocol *p, pa_native_connection *c, struct userdata *u) {
998 pa_assert(p);
999 pa_assert(c);
1000 pa_assert(u);
1001
1002 pa_idxset_remove_by_data(u->subscribed, c, NULL);
1003 return PA_HOOK_OK;
1004 }
1005
1006 int pa__init(pa_module*m) {
1007 pa_modargs *ma = NULL;
1008 struct userdata *u;
1009 char *fname;
1010 pa_sink_input *si;
1011 pa_source_output *so;
1012 uint32_t idx;
1013 pa_bool_t restore_device = TRUE, restore_volume = TRUE, restore_muted = TRUE, on_hotplug = TRUE, on_rescue = TRUE;
1014
1015 pa_assert(m);
1016
1017 if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
1018 pa_log("Failed to parse module arguments");
1019 goto fail;
1020 }
1021
1022 if (pa_modargs_get_value_boolean(ma, "restore_device", &restore_device) < 0 ||
1023 pa_modargs_get_value_boolean(ma, "restore_volume", &restore_volume) < 0 ||
1024 pa_modargs_get_value_boolean(ma, "restore_muted", &restore_muted) < 0 ||
1025 pa_modargs_get_value_boolean(ma, "on_hotplug", &on_hotplug) < 0 ||
1026 pa_modargs_get_value_boolean(ma, "on_rescue", &on_rescue) < 0) {
1027 pa_log("restore_device=, restore_volume=, restore_muted=, on_hotplug= and on_rescue= expect boolean arguments");
1028 goto fail;
1029 }
1030
1031 if (!restore_muted && !restore_volume && !restore_device)
1032 pa_log_warn("Neither restoring volume, nor restoring muted, nor restoring device enabled!");
1033
1034 m->userdata = u = pa_xnew0(struct userdata, 1);
1035 u->core = m->core;
1036 u->module = m;
1037 u->restore_device = restore_device;
1038 u->restore_volume = restore_volume;
1039 u->restore_muted = restore_muted;
1040 u->on_hotplug = on_hotplug;
1041 u->on_rescue = on_rescue;
1042 u->subscribed = pa_idxset_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
1043
1044 u->protocol = pa_native_protocol_get(m->core);
1045 pa_native_protocol_install_ext(u->protocol, m, extension_cb);
1046
1047 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);
1048
1049 u->subscription = pa_subscription_new(m->core, PA_SUBSCRIPTION_MASK_SINK_INPUT|PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT, subscribe_callback, u);
1050
1051 if (restore_device) {
1052 /* A little bit earlier than module-intended-roles ... */
1053 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);
1054 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);
1055 }
1056
1057 if (restore_device && on_hotplug) {
1058 /* A little bit earlier than module-intended-roles ... */
1059 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);
1060 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);
1061 }
1062
1063 if (restore_device && on_rescue) {
1064 /* A little bit earlier than module-intended-roles, module-rescue-streams, ... */
1065 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);
1066 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);
1067 }
1068
1069 if (restore_volume || restore_muted)
1070 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);
1071
1072 if (!(fname = pa_state_path("stream-volumes", TRUE)))
1073 goto fail;
1074
1075 if (!(u->database = pa_database_open(fname, TRUE))) {
1076 pa_log("Failed to open volume database '%s': %s", fname, pa_cstrerror(errno));
1077 pa_xfree(fname);
1078 goto fail;
1079 }
1080
1081 pa_log_info("Sucessfully opened database file '%s'.", fname);
1082 pa_xfree(fname);
1083
1084 PA_IDXSET_FOREACH(si, m->core->sink_inputs, idx)
1085 subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SINK_INPUT|PA_SUBSCRIPTION_EVENT_NEW, si->index, u);
1086
1087 PA_IDXSET_FOREACH(so, m->core->source_outputs, idx)
1088 subscribe_callback(m->core, PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT|PA_SUBSCRIPTION_EVENT_NEW, so->index, u);
1089
1090 pa_modargs_free(ma);
1091 return 0;
1092
1093 fail:
1094 pa__done(m);
1095
1096 if (ma)
1097 pa_modargs_free(ma);
1098
1099 return -1;
1100 }
1101
1102 void pa__done(pa_module*m) {
1103 struct userdata* u;
1104
1105 pa_assert(m);
1106
1107 if (!(u = m->userdata))
1108 return;
1109
1110 if (u->subscription)
1111 pa_subscription_free(u->subscription);
1112
1113 if (u->sink_input_new_hook_slot)
1114 pa_hook_slot_free(u->sink_input_new_hook_slot);
1115 if (u->sink_input_fixate_hook_slot)
1116 pa_hook_slot_free(u->sink_input_fixate_hook_slot);
1117 if (u->source_output_new_hook_slot)
1118 pa_hook_slot_free(u->source_output_new_hook_slot);
1119
1120 if (u->sink_put_hook_slot)
1121 pa_hook_slot_free(u->sink_put_hook_slot);
1122 if (u->source_put_hook_slot)
1123 pa_hook_slot_free(u->source_put_hook_slot);
1124
1125 if (u->sink_unlink_hook_slot)
1126 pa_hook_slot_free(u->sink_unlink_hook_slot);
1127 if (u->source_unlink_hook_slot)
1128 pa_hook_slot_free(u->source_unlink_hook_slot);
1129
1130 if (u->connection_unlink_hook_slot)
1131 pa_hook_slot_free(u->connection_unlink_hook_slot);
1132
1133 if (u->save_time_event)
1134 u->core->mainloop->time_free(u->save_time_event);
1135
1136 if (u->database)
1137 pa_database_close(u->database);
1138
1139 if (u->protocol) {
1140 pa_native_protocol_remove_ext(u->protocol, m);
1141 pa_native_protocol_unref(u->protocol);
1142 }
1143
1144 if (u->subscribed)
1145 pa_idxset_free(u->subscribed, NULL, NULL);
1146
1147 pa_xfree(u);
1148 }