/*** Volume Change Notification Module for PulseAudio Copyright 2012 Alexander Sulfrian This module is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This module is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with PulseAudio; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. ***/ #include #include #include #include #include PA_MODULE_AUTHOR("Alexander Sulfrian"); PA_MODULE_DESCRIPTION("Send DBus signals on volume change."); PA_MODULE_VERSION(PACKAGE_VERSION); PA_MODULE_USAGE(""); struct userdata { pa_core *core; pa_module *module; pa_subscription *subscription; /* dbus stuff */ DBusError dbus_error; DBusConnection *dbus_connection; }; dbus_bool_t add_volumes(DBusMessageIter *args, const pa_cvolume *vol) { DBusMessageIter array; char buf[] = { DBUS_TYPE_UINT16, 0}; const pa_volume_t *values; uint8_t channel; if (!dbus_message_iter_open_container (args, DBUS_TYPE_ARRAY, buf, &array)) return FALSE; for (channel = 0; channel < vol->channels; channel++) { uint16_t volume = (vol->values[channel] * 100 + PA_VOLUME_NORM / 2) / PA_VOLUME_NORM; if (!dbus_message_iter_append_basic(&array, DBUS_TYPE_UINT16, &volume)) { dbus_message_iter_abandon_container (args, &array); return FALSE; } } if (!dbus_message_iter_close_container (args, &array)) return FALSE; return TRUE; } void send_dbus_signal(const char *name, const pa_bool_t muted, const pa_cvolume *vol, const struct userdata *u) { DBusMessage* msg; DBusMessageIter args; /* create a signal and check for errors */ msg = dbus_message_new_signal("/org/PulseAudio/VolumeNotification", "org.PulseAudio.VolumeNotification", "VolumeChange"); if (!msg) return; /* append arguments onto signal */ dbus_message_iter_init_append(msg, &args); if (!dbus_message_iter_append_basic(&args, DBUS_TYPE_STRING, &name)) goto fail; if (!dbus_message_iter_append_basic(&args, DBUS_TYPE_BOOLEAN, &muted)) goto fail; if (!add_volumes(&args, vol)) goto fail; /* send the message and flush the connection */ if (!dbus_connection_send(u->dbus_connection, msg, NULL)) goto fail; dbus_connection_flush(u->dbus_connection); fail: /* free the message */ dbus_message_unref(msg); } void sink_change_event_cb(pa_core *c, pa_subscription_event_type_t t, uint32_t idx, void *userdata) { pa_sink *s; if (PA_SUBSCRIPTION_EVENT_CHANGE & t == 0) return; if (!userdata) return; s = pa_idxset_get_by_index(c->sinks, idx); if (!s) return; send_dbus_signal(s->name, s->muted, &s->reference_volume, userdata); } void pa__done(pa_module *m) { struct userdata *u; if (!(u = m->userdata)) return; pa_subscription_free(u->subscription); if (u->dbus_connection) dbus_connection_close(u->dbus_connection); pa_xfree(u); } int pa__init(pa_module* m) { struct userdata *u; pa_subscription_mask_t mask; int ret; m->userdata = u = pa_xnew0(struct userdata, 1); u->core = m->core; u->module = m; mask = PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT | PA_SUBSCRIPTION_EVENT_CHANGE; u->subscription = pa_subscription_new(m->core, mask, sink_change_event_cb, u); /* initialize dbus */ dbus_error_init(&u->dbus_error); u->dbus_connection = dbus_bus_get(DBUS_BUS_SESSION, &u->dbus_error); if (dbus_error_is_set(&u->dbus_error)) { pa_log("DBus Connection Error (%s)\n", u->dbus_error.message); dbus_error_free(&u->dbus_error); } if (!u->dbus_connection) goto fail; /* request a name on the bus */ ret = dbus_bus_request_name(u->dbus_connection, "org.PulseAudio.VolumeNotification", DBUS_NAME_FLAG_REPLACE_EXISTING, &u->dbus_error); if (dbus_error_is_set(&u->dbus_error)) { pa_log("DBus Name Error (%s)\n", u->dbus_error.message); dbus_error_free(&u->dbus_error); } if (DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER != ret) { goto fail; } return 0; fail: pa__done(m); return -1; }