/* * Copyright 2010 Canonical Ltd. * * Authors: * Conor Curran <conor.curran@canonical.com> * * This program is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3, as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranties of * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see <http://www.gnu.org/licenses/>. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include <gio/gio.h> #include <unistd.h> #include <glib/gi18n.h> #include <libindicator/indicator-service.h> #include <libdbusmenu-glib/server.h> #include <libdbusmenu-glib/client.h> #include "sound-service-dbus.h" #include "device.h" #include "gen-sound-service.xml.h" #include "dbus-shared-names.h" // DBUS methods static void bus_method_call (GDBusConnection * connection, const gchar * sender, const gchar * path, const gchar * interface, const gchar * method, GVariant * params, GDBusMethodInvocation * invocation, gpointer user_data); static GDBusInterfaceVTable interface_table = { method_call: bus_method_call, get_property: NULL, /* No properties */ set_property: NULL /* No properties */ }; typedef struct _SoundServiceDbusPrivate SoundServiceDbusPrivate; struct _SoundServiceDbusPrivate { GDBusConnection* connection; DbusmenuMenuitem* root_menuitem; Device* device; }; static GDBusNodeInfo * node_info = NULL; static GDBusInterfaceInfo * interface_info = NULL; #define SOUND_SERVICE_DBUS_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUND_SERVICE_DBUS_TYPE, SoundServiceDbusPrivate)) static void sound_service_dbus_class_init (SoundServiceDbusClass *klass); static void sound_service_dbus_init (SoundServiceDbus *self); static void sound_service_dbus_dispose (GObject *object); static void sound_service_dbus_finalize (GObject *object); static void show_sound_settings_dialog (DbusmenuMenuitem *mi, gpointer user_data); static gboolean sound_service_dbus_blacklist_player (SoundServiceDbus* self, gchar* player_name, gboolean blacklist); static gboolean sound_service_dbus_is_blacklisted (SoundServiceDbus* self, gchar* player_name); G_DEFINE_TYPE (SoundServiceDbus, sound_service_dbus, G_TYPE_OBJECT); static void sound_service_dbus_class_init (SoundServiceDbusClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); g_type_class_add_private (object_class, sizeof(SoundServiceDbusPrivate)); object_class->dispose = sound_service_dbus_dispose; object_class->finalize = sound_service_dbus_finalize; g_assert(klass != NULL); if (node_info == NULL) { GError * error = NULL; node_info = g_dbus_node_info_new_for_xml(_sound_service, &error); if (error != NULL) { g_error("Unable to parse Indicator Service Interface description: %s", error->message); g_error_free(error); } } if (interface_info == NULL) { interface_info = g_dbus_node_info_lookup_interface (node_info, INDICATOR_SOUND_DBUS_INTERFACE); if (interface_info == NULL) { g_error("Unable to find interface '" INDICATOR_SOUND_DBUS_INTERFACE "'"); } } } static void sound_service_dbus_init (SoundServiceDbus *self) { GError *error = NULL; SoundServiceDbusPrivate * priv = SOUND_SERVICE_DBUS_GET_PRIVATE(self); priv->connection = NULL; /* Fetch the session bus */ priv->connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error); if (error != NULL) { g_error("sound-service-dbus:Unable to connect to the session bus when creating indicator sound service : %s", error->message); g_error_free(error); return; } /* register the service on it */ g_dbus_connection_register_object (priv->connection, INDICATOR_SOUND_SERVICE_DBUS_OBJECT_PATH, interface_info, &interface_table, self, NULL, &error); if (error != NULL) { g_error("Unable to register the object to DBus: %s", error->message); g_error_free(error); return; } } DbusmenuMenuitem* sound_service_dbus_create_root_item (SoundServiceDbus* self) { SoundServiceDbusPrivate * priv = SOUND_SERVICE_DBUS_GET_PRIVATE(self); priv->root_menuitem = dbusmenu_menuitem_new(); g_debug("Root ID: %d", dbusmenu_menuitem_get_id(priv->root_menuitem)); DbusmenuServer *server = dbusmenu_server_new (INDICATOR_SOUND_MENU_DBUS_OBJECT_PATH); const gchar* paths[] = {"/usr/share/banshee-1/icons", NULL}; dbusmenu_server_set_icon_paths (server, paths); dbusmenu_server_set_root (server, priv->root_menuitem); g_object_unref (priv->root_menuitem); priv->device = device_new (self); return priv->root_menuitem; } void sound_service_dbus_build_sound_menu ( SoundServiceDbus* self, DbusmenuMenuitem* mute_item, DbusmenuMenuitem* slider_item, DbusmenuMenuitem* voip_input_menu_item) { SoundServiceDbusPrivate * priv = SOUND_SERVICE_DBUS_GET_PRIVATE(self); // Mute button // TODO this additions should be fixed position, i.e. add via position and not just append // be explicit as it is fixed. dbusmenu_menuitem_child_append (priv->root_menuitem, mute_item); dbusmenu_menuitem_child_append (priv->root_menuitem, slider_item); g_debug ("just about to add the slider %i", DBUSMENU_IS_MENUITEM(slider_item)); dbusmenu_menuitem_child_append (priv->root_menuitem, voip_input_menu_item); // Separator DbusmenuMenuitem* separator = dbusmenu_menuitem_new(); dbusmenu_menuitem_property_set (separator, DBUSMENU_MENUITEM_PROP_TYPE, DBUSMENU_CLIENT_TYPES_SEPARATOR); dbusmenu_menuitem_child_append(priv->root_menuitem, separator); g_object_unref (separator); // Sound preferences dialog DbusmenuMenuitem* settings_mi = dbusmenu_menuitem_new(); dbusmenu_menuitem_property_set( settings_mi, DBUSMENU_MENUITEM_PROP_LABEL, _("Sound Preferences...")); dbusmenu_menuitem_child_append(priv->root_menuitem, settings_mi); g_object_unref (settings_mi); g_signal_connect(G_OBJECT(settings_mi), DBUSMENU_MENUITEM_SIGNAL_ITEM_ACTIVATED, G_CALLBACK(show_sound_settings_dialog), NULL); } /** show_sound_settings_dialog: Bring up the gnome volume preferences dialog **/ static void show_sound_settings_dialog (DbusmenuMenuitem *mi, gpointer user_data) { GError * error = NULL; if (!g_spawn_command_line_async("gnome-volume-control --page=applications", &error) && !g_spawn_command_line_async("xfce4-mixer", &error)) { g_warning("Unable to show dialog: %s", error->message); g_error_free(error); } } static void sound_service_dbus_dispose (GObject *object) { G_OBJECT_CLASS (sound_service_dbus_parent_class)->dispose (object); //TODO dispose of the active sink instance ! return; } static void sound_service_dbus_finalize (GObject *object) { G_OBJECT_CLASS (sound_service_dbus_parent_class)->finalize (object); return; } // EMIT STATE SIGNAL void sound_service_dbus_update_sound_state (SoundServiceDbus* self, SoundState new_state) { SoundServiceDbusPrivate *priv = SOUND_SERVICE_DBUS_GET_PRIVATE (self); GVariant* v_output = g_variant_new("(i)", (int)new_state); GError * error = NULL; g_debug ("emitting state signal with value %i", (int)new_state); g_dbus_connection_emit_signal( priv->connection, NULL, INDICATOR_SOUND_SERVICE_DBUS_OBJECT_PATH, INDICATOR_SOUND_DBUS_INTERFACE, INDICATOR_SOUND_SIGNAL_STATE_UPDATE, v_output, &error ); if (error != NULL) { g_error("Unable to emit signal 'sinkinputwhilemuted' because : %s", error->message); g_error_free(error); return; } } //HANDLE DBUS METHOD CALLS static void bus_method_call (GDBusConnection * connection, const gchar * sender, const gchar * path, const gchar * interface, const gchar * method, GVariant * params, GDBusMethodInvocation * invocation, gpointer user_data) { SoundServiceDbus* service = SOUND_SERVICE_DBUS(user_data); g_return_if_fail ( IS_SOUND_SERVICE_DBUS(service) ); GVariant * retval = NULL; SoundServiceDbusPrivate *priv = SOUND_SERVICE_DBUS_GET_PRIVATE (service); if (g_strcmp0(method, "GetSoundState") == 0) { g_debug("Get state - %i", device_get_state (priv->device)); retval = g_variant_new ( "(i)", device_get_state (priv->device)); } else if (g_strcmp0(method, "BlacklistMediaPlayer") == 0) { gboolean blacklist; gchar* player_name; g_variant_get (params, "(sb)", &player_name, &blacklist); g_debug ("BlacklistMediaPlayer - bool %i", blacklist); g_debug ("BlacklistMediaPlayer - name %s", player_name); gboolean result = sound_service_dbus_blacklist_player (service, player_name, blacklist); retval = g_variant_new ("(b)", result); } else if (g_strcmp0(method, "IsBlacklisted") == 0) { gchar* player_name; g_variant_get (params, "(s)", &player_name); g_debug ("IsBlacklisted - name %s", player_name); gboolean result = sound_service_dbus_is_blacklisted (service, player_name); retval = g_variant_new ("(b)", result); } else { g_warning("Calling method '%s' on the sound service but it's unknown", method); } g_dbus_method_invocation_return_value (invocation, retval); } /** TODO - Works nicely but refactor into at least two different methods **/ static gboolean sound_service_dbus_blacklist_player (SoundServiceDbus* self, gchar* player_name, gboolean blacklist) { g_return_val_if_fail (player_name != NULL, FALSE); g_return_val_if_fail (IS_SOUND_SERVICE_DBUS (self), FALSE); GVariant* the_black_list; gboolean result = FALSE; GSettings* our_settings; GVariantIter iter; gchar *str; GVariantBuilder builder; our_settings = g_settings_new ("com.canonical.indicators.sound"); the_black_list = g_settings_get_value (our_settings, "blacklisted-media-players"); g_variant_iter_init (&iter, the_black_list); g_variant_builder_init(&builder, G_VARIANT_TYPE_STRING_ARRAY); while (g_variant_iter_loop (&iter, "s", &str)){ g_variant_builder_add (&builder, "s", str); } g_variant_iter_init (&iter, the_black_list); if (blacklist == TRUE){ while (g_variant_iter_loop (&iter, "s", &str)){ g_print ("first pass to check if %s is present\n", str); if (g_strcmp0 (player_name, str) == 0){ // Return if its already there g_debug ("we have this already blacklisted, no need to do anything"); g_variant_builder_clear (&builder); g_object_unref (our_settings); g_variant_unref (the_black_list); return result; } } // Otherwise blacklist it ! g_debug ("about to blacklist %s", player_name); g_variant_builder_add (&builder, "s", player_name); } else{ gboolean present = FALSE; g_variant_iter_init (&iter, the_black_list); g_debug ("attempting to UN-blacklist %s", player_name); while (g_variant_iter_loop (&iter, "s", &str)){ if (g_strcmp0 (player_name, str) == 0){ present = TRUE; } } // It was not there anyway, return false if (present == FALSE){ g_debug ("it was not blacklisted ?, no need to do anything"); g_variant_builder_clear (&builder); g_object_unref (our_settings); g_variant_unref (the_black_list); return result; } // Otherwise free the builder and reconstruct ensuring no duplicates. g_variant_builder_clear (&builder); g_variant_builder_init (&builder, G_VARIANT_TYPE_STRING_ARRAY); g_variant_iter_init (&iter, the_black_list); while (g_variant_iter_loop (&iter, "s", &str)){ if (g_strcmp0 (player_name, str) != 0){ g_variant_builder_add (&builder, "s", str); } } } GVariant* value = g_variant_builder_end (&builder); result = g_settings_set_value (our_settings, "blacklisted-media-players", value); g_object_unref (our_settings); g_variant_unref (the_black_list); return result; } static gboolean sound_service_dbus_is_blacklisted (SoundServiceDbus *self, gchar *player_name) { GSettings *our_settings; GVariant *the_black_list; GVariantIter iter; gchar *str; gboolean result = FALSE; g_return_val_if_fail (player_name != NULL, FALSE); g_return_val_if_fail (IS_SOUND_SERVICE_DBUS (self), FALSE); our_settings = g_settings_new ("com.canonical.indicators.sound"); the_black_list = g_settings_get_value (our_settings, "blacklisted-media-players"); g_variant_iter_init (&iter, the_black_list); while (g_variant_iter_next (&iter, "s", &str)){ if (g_strcmp0 (player_name, str) == 0) { result = TRUE; g_free (str); break; } g_free (str); } g_object_unref (our_settings); g_variant_unref (the_black_list); return result; }