+ pa_log_info("Successfully moved %s for \"%s\" to <%s>.", is_sink_input ? "sink-input" : "source-output",
+ pa_strnull(pa_proplist_gets(pl, PA_PROP_APPLICATION_NAME)), name);
+
+ pa_proplist_unset(pl, PA_PROP_FILTER_APPLY_MOVING);
+}
+
+static void move_objects_for_filter(struct userdata *u, pa_object *o, struct filter* filter, bool restore,
+ bool is_sink_input) {
+
+ if (!should_group_filter(filter))
+ move_object_for_filter(o, filter, restore, is_sink_input);
+ else {
+ pa_source_output *so;
+ pa_sink_input *si;
+ char *g, *group;
+ uint32_t idx;
+
+ group = get_group(o, is_sink_input);
+
+ PA_IDXSET_FOREACH(so, u->core->source_outputs, idx) {
+ g = get_group(PA_OBJECT(so), false);
+
+ if (pa_streq(g, group))
+ move_object_for_filter(PA_OBJECT(so), filter, restore, false);
+
+ pa_xfree(g);
+ }
+
+ PA_IDXSET_FOREACH(si, u->core->sink_inputs, idx) {
+ g = get_group(PA_OBJECT(si), true);
+
+ if (pa_streq(g, group))
+ move_object_for_filter(PA_OBJECT(si), filter, restore, true);
+
+ pa_xfree(g);
+ }
+
+ pa_xfree(group);
+ }
+}
+
+/* Note that we assume a filter will provide at most one sink and at most one
+ * source (and at least one of either). */
+static void find_filters_for_module(struct userdata *u, pa_module *m, const char *name) {
+ uint32_t idx;
+ pa_sink *sink;
+ pa_source *source;
+ struct filter *fltr = NULL;
+
+ PA_IDXSET_FOREACH(sink, u->core->sinks, idx) {
+ if (sink->module == m) {
+ pa_assert(sink->input_to_master != NULL);
+
+ fltr = filter_new(name, sink->input_to_master->sink, NULL);
+ fltr->module_index = m->index;
+ fltr->sink = sink;
+
+ break;
+ }
+ }
+
+ PA_IDXSET_FOREACH(source, u->core->sources, idx) {
+ if (source->module == m && !source->monitor_of) {
+ pa_assert(source->output_from_master != NULL);
+
+ if (!fltr) {
+ fltr = filter_new(name, NULL, source->output_from_master->source);
+ fltr->module_index = m->index;
+ fltr->source = source;
+ } else {
+ fltr->source = source;
+ fltr->source_master = source->output_from_master->source;
+ }
+
+ break;
+ }
+ }
+
+ pa_hashmap_put(u->filters, fltr, fltr);
+}
+
+static bool can_unload_module(struct userdata *u, uint32_t idx) {
+ void *state;
+ struct filter *filter;
+
+ /* Check if any other struct filters point to the same module */
+ PA_HASHMAP_FOREACH(filter, u->filters, state) {
+ if (filter->module_index == idx && !nothing_attached(filter))
+ return false;
+ }
+
+ return true;