/* * Copyright (c) Linux community. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #include "dbus-player.h" #include "dbus-mpris.h" #include "dbus-banshee.h" #include "dbus-marshal.h" #include "log.h" #include "support.h" #include "utility.h" // Thanks to: // http://code.google.com/p/pidgin-musictracker/source/browse/trunk/src/banshee.c?r=446 // // Read also dev-info/banshee_readme.txt void marshal_VOID__STRING_STRING_DOUBLE (GClosure *closure, GValue *return_value G_GNUC_UNUSED, guint n_param_values, const GValue *param_values, gpointer invocation_hint G_GNUC_UNUSED,gpointer marshal_data); void banshee_player_track_changed(MediaPlayerRec *player) { // Called when track changes // Re-read all player data banshee_get_info(player); // Set track changed signal TrackInfo *tr = &player->track; tr->status = PLAYER_STATUS_TRACK_CHANGE; // Print debug info // dbus_player_debug_print(player); // Process data dbus_player_process_data(player); } void banshee_player_status_changed(MediaPlayerRec *player) { // Called when player's status or track changes // Re-read all player data banshee_get_info(player); // Print debug info // dbus_player_debug_print(player); // Process data dbus_player_process_data(player); } void banshee_get_app_name(MediaPlayerRec *player) { // Name of the Banshee media player. player->app_name = g_strdup(_("Banshee Media Player")); } void banshee_state_signal_cb(DBusGProxy *player_proxy, gchar *state, MediaPlayerRec *player) { // State signal from Banshee // state: // "idle" (quit player) // "loading" // "loaded" // "playing" (play) // "paused" (pause) if ((!g_strcmp0(state, "idle")) || // Quit player (!g_strcmp0(state, "playing")) || // Play (!g_strcmp0(state, "paused"))) { // We do not utilize these state-events at the moment. See the banshee_event_signal_cb() function below. // Re-read all data and inform GUI // banshee_player_status_changed(player); } } void banshee_event_signal_cb(DBusGProxy *player_proxy, gchar *event, gchar *message, gdouble buffering_percentage, MediaPlayerRec *player) { // Event signal from Banshee // event: // "volume" // "seek" // "statechange" // "requestnexttrack" // "startofstream" // "endofstream" LOG_PLAYER("banshee_event_signal_cb: %s (%s) got event:<%s>\n", player->app_name, player->service_name, event); if (!g_strcmp0(event, "volume")) { // Do nothing } else if (!g_strcmp0(event, "seek")) { // Do nothing } else if (!g_strcmp0(event, "requestnexttrack")) { // Do nothing } else if (!g_strcmp0(event, "statechange")) { // Re-read all data and inform the recorder banshee_player_status_changed(player); } else if (!g_strcmp0(event, "startofstream")) { // Re-read all data and inform the recorder banshee_player_status_changed(player); } else if (!g_strcmp0(event, "endofstream")) { // Track changed. Inform the recorder banshee_player_track_changed(player); } } gboolean banshee_check_proxy(MediaPlayerRec *player) { // Check/set proxy if (!player) return FALSE; if (!player->proxy) { DBusGConnection *dbus_conn = dbus_player_connect_to_dbus(); player->proxy = dbus_g_proxy_new_for_name(dbus_conn, player->service_name, "/org/bansheeproject/Banshee/PlayerEngine", "org.bansheeproject.Banshee.PlayerEngine"); } if (!player->proxy) { LOG_ERROR("banshee_check_proxy: Cannot create DBus proxy for Banshee.\n"); } return (player->proxy != NULL); } void banshee_set_signals(gpointer player_rec, gboolean connect) { // Connect/disconnect signals MediaPlayerRec *player = (MediaPlayerRec*)player_rec; if (!player) return; if (!player->proxy) { if (!banshee_check_proxy(player)) return; // Register arguments. dbus_g_object_register_marshaller(marshal_VOID__STRING_STRING_DOUBLE, G_TYPE_NONE, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_DOUBLE, G_TYPE_INVALID); dbus_g_proxy_add_signal(player->proxy, "StateChanged", G_TYPE_STRING, G_TYPE_INVALID); dbus_g_proxy_add_signal(player->proxy, "EventChanged", G_TYPE_STRING, G_TYPE_STRING, G_TYPE_DOUBLE, G_TYPE_INVALID); } if (!banshee_check_proxy(player)) return; if (connect) { // Connect signals dbus_g_proxy_connect_signal(player->proxy, "StateChanged", G_CALLBACK(banshee_state_signal_cb), (gpointer)player, NULL); dbus_g_proxy_connect_signal(player->proxy, "EventChanged", G_CALLBACK(banshee_event_signal_cb), (gpointer)player, NULL); } else { // Disconnect signals dbus_g_proxy_disconnect_signal(player->proxy, "StateChanged", G_CALLBACK(banshee_state_signal_cb), (gpointer)player); dbus_g_proxy_disconnect_signal(player->proxy, "EventChanged", G_CALLBACK(banshee_event_signal_cb), (gpointer)player); } } void banshee_hash_str(GHashTable *table, gchar *key, gchar *dest) { dest[0] = '\0'; GValue* value = (GValue*) g_hash_table_lookup(table, key); if (G_VALUE_HOLDS_STRING(value)) { str_copy(dest, (gchar*)g_value_get_string(value), MPRIS_STRLEN-1); } } gboolean banshee_dbus_string(DBusGProxy *proxy, gchar *method, gchar* dest) { dest[0] = '\0'; gchar *str = NULL; GError *error = 0; if (!dbus_g_proxy_call_with_timeout (proxy, method, DBUS_MPRIS_TIMEOUT, &error, G_TYPE_INVALID, G_TYPE_STRING, &str, G_TYPE_INVALID)) { LOG_ERROR("banshee_dbus_string: Failed to make DBus call %s: %s", method, error ? error->message : ""); g_free(str); str = NULL; if (error) g_error_free(error); return FALSE; } str_copy(dest, str, MPRIS_STRLEN-1); dest[MPRIS_STRLEN-1] = '\0'; g_free(str); return TRUE; } gint banshee_dbus_int(DBusGProxy *proxy, const char *method) { gint ret = 0; GError *error = NULL; if (!dbus_g_proxy_call_with_timeout (proxy, method, DBUS_MPRIS_TIMEOUT, &error, G_TYPE_INVALID, G_TYPE_INT, &ret, G_TYPE_INVALID)) { LOG_ERROR("banshee_dbus_int: Failed to make DBus call %s: %s", method, error ? error->message : ""); if (error) g_error_free(error); return 0; } return ret; } guint banshee_dbus_uint(DBusGProxy *proxy, const char *method) { guint ret = 0; GError *error = NULL; if (!dbus_g_proxy_call_with_timeout (proxy, method, DBUS_MPRIS_TIMEOUT, &error, G_TYPE_INVALID, G_TYPE_UINT, &ret, G_TYPE_INVALID)) { LOG_ERROR("banshee_dbus_uint: Failed to make DBus call %s: %s", method, error ? error->message : ""); if (error) g_error_free(error); return 0; } return ret; } void banshee_get_info(gpointer player_rec) { MediaPlayerRec *player = (MediaPlayerRec*)player_rec; TrackInfo *tr = &player->track; // Is this player running/active? player->active = mpris_service_is_running_by_name(player->service_name); if (!player->active) { tr->status = PLAYER_STATUS_CLOSED; goto LBL_1; } // Check proxy if (!banshee_check_proxy(player)) return; char str[MPRIS_STRLEN]; if (!banshee_dbus_string(player->proxy, "GetCurrentState", str)) { // Application has exited/shutdown player->active = FALSE; tr->status = PLAYER_STATUS_STOPPED; goto LBL_1; } if (!g_strcmp0(str, "idle")) { tr->status = PLAYER_STATUS_STOPPED; goto LBL_1; } else if (!g_strcmp0(str, "playing")) { tr->status = PLAYER_STATUS_PLAYING; } else { tr->status = PLAYER_STATUS_PAUSED; } GError *error = NULL; GHashTable* table = NULL; if (!dbus_g_proxy_call_with_timeout(player->proxy, "GetCurrentTrack", DBUS_MPRIS_TIMEOUT, &error, G_TYPE_INVALID, dbus_g_type_get_map("GHashTable", G_TYPE_STRING, G_TYPE_VALUE), &table,G_TYPE_INVALID)) { LOG_ERROR("banshee_get_info: Failed to make DBus call: %s", error ? error->message : ""); if (error) g_error_free(error); goto LBL_1; } banshee_hash_str(table, "album", tr->album); banshee_hash_str(table, "artist", tr->artist); banshee_hash_str(table, "name", tr->track); g_hash_table_destroy(table); tr->total_secs = banshee_dbus_uint(player->proxy, "GetLength") / 1000; tr->current_secs = banshee_dbus_uint(player->proxy, "GetPosition") / 1000; LBL_1: ; } void banshee_start_app(gpointer player_rec) { // Start Banshee MediaPlayerRec *player = (MediaPlayerRec*)player_rec; // Check/set proxy if (!banshee_check_proxy(player)) return; dbus_g_proxy_call_no_reply(player->proxy, "Start", G_TYPE_INVALID); } /* VOID:STRING,STRING,DOUBLE (marshal.list:1) Ref: http://web.archive.org/web/20080501043040/http://wiki.bluez.org/wiki/HOWTO/DiscoveringDevices An example: $ echo "VOID:STRING,STRING,DOUBLE" > marshal.list $ glib-genmarshal --prefix=marshal marshal.list --header > sample1.h $ glib-genmarshal --prefix=marshal marshal.list --body > sample1.c See dbus_marshal.c */ void marshal_VOID__STRING_STRING_DOUBLE (GClosure *closure, GValue *return_value G_GNUC_UNUSED, guint n_param_values, const GValue *param_values, gpointer invocation_hint G_GNUC_UNUSED, gpointer marshal_data) { typedef void (*GMarshalFunc_VOID__STRING_STRING_DOUBLE) (gpointer data1, gpointer arg_1, gpointer arg_2, gdouble arg_3, gpointer data2); register GMarshalFunc_VOID__STRING_STRING_DOUBLE callback; register GCClosure *cc = (GCClosure*) closure; register gpointer data1, data2; g_return_if_fail (n_param_values == 4); if (G_CCLOSURE_SWAP_DATA (closure)) { data1 = closure->data; data2 = g_value_peek_pointer (param_values + 0); } else { data1 = g_value_peek_pointer (param_values + 0); data2 = closure->data; } callback = (GMarshalFunc_VOID__STRING_STRING_DOUBLE) (marshal_data ? marshal_data : cc->callback); callback (data1, g_marshal_value_peek_string (param_values + 1), g_marshal_value_peek_string (param_values + 2), g_marshal_value_peek_double (param_values + 3), data2); }